The noexcept operator was introduced in C++11 to perform a compile-time check of an expression's exception specification. It determines whether an expression is considered non-throwing without actually evaluating it.
- Returns true if the expression is guaranteed not to throw exceptions.
- Returns false if the expression may throw an exception.
- Does not execute or evaluate the expression being checked.
#include <iostream>
using namespace std;
// Function that may throw an exception
int divide(int a, int b)
{
if (b == 0) {
throw runtime_error("Error: Division by zero");
}
return a / b;
}
// Function that will not throw any exceptions (using
// noexcept)
double safeDivide(double a, double b) noexcept
{
if (b == 0) {
// In this case, std::terminate will be called if b
// is zero
cerr << "Error: Division by zero in safeDivide"
<< std::endl;
terminate();
}
return a / b;
}
int main()
{
cout << "noexcept value for divide(): "
<< noexcept(divide(10, 0)) << "\n";
cout << "noexcept value for safeDivide(): "
<< noexcept(safeDivide(10.0, 0.0));
return 0;
}
Output
noexcept value for divide(): 0 noexcept value for safeDivide(): 1
Explanation
- divide() is not declared with the noexcept specifier.
- safeDivide() is declared as noexcept.
- The noexcept operator checks the exception specification of both functions at compile time.
- Therefore, noexcept(divide(10, 0)) evaluates to false and noexcept(safeDivide(10.0, 0.0)) evaluates to true.
Note: If an exception escapes from a function declared noexcept, the program calls std::terminate().
Syntax
noexcept(expression)
Where, expression is the expression whose exception specification is checked.
Return Value
- true if the expression is declared as non-throwing.
- false if the expression may throw an exception.
Using noexcept with Function Templates
The noexcept operator is often used in templates to conditionally determine whether a function should be marked as noexcept based on the operations it performs.
#include <bits/stdc++.h>
using namespace std;
template <typename T>
void process(const T& value)
noexcept(noexcept(declval<T>().size()))
{
// Try to call the size() member function of T
// If T's size() can throw, this function won't be
// noexcept for T
// If T's size() is noexcept, this
// function will be noexcept for T
cout << "Size: " << value.size() << endl;
}
// main function
int main()
{
vector<int> numbers = { 1, 2, 3, 4, 5 };
// Won't throw exceptions for std::vector
process(numbers);
// May throw exceptions for int
process(42);
return 0;
}
Output
./Solution.cpp: In instantiation of 'void process(const T&) [with T = int]':
./Solution.cpp:23:15: required from here
./Solution.cpp:5:6: error: request for member 'size' in 'std::declval<int>()', which is of non-class type 'int'
void process(const T& value)
^
./Solution.cpp: In instantiation of 'void process(const T&) [with T = int]':
./Solution.cpp:23:15: required from here
./Solution.cpp:5:6: error: request for member 'size' in 'std::declval<int>()', which is of non-class type 'int...
Explanation
- noexcept(declval<T>().size()) checks whether calling size() on type T is non-throwing.
- The result is used to determine whether process() should be declared noexcept.
- The compilation error occurs because int does not have a size() member function.
- The error is unrelated to exception handling; it occurs because the expression itself is invalid for int.
When to Use noexcept
Use noexcept when:
- A function is guaranteed not to throw exceptions.
- Writing move constructors or move assignment operators for better performance.
- Creating generic libraries and templates that depend on exception guarantees.
- You want to enable compiler optimizations for non-throwing functions.
When Not to Use noexcept
Avoid using noexcept when:
- The function can legitimately throw exceptions.
- You are unsure whether all operations inside the function are exception-safe.
- The function relies on third-party code that may throw exceptions.