1. Problem It Solves
Unclear operand evaluation can make side effects surprising, while exception specifications previously had limited participation in type relationships. C++17 strengthens several sequencing rules and makes noexcept part of a function type.
This lesson reduces that broad problem to one fixed-input program so the language rule and its observable result can be checked independently.
2. Prerequisites
A C++17 compiler invoked with warnings enabled and the earlier lessons listed in the course order.
Know side effects, sequencing, function pointers, exception specifications, and
std::is_nothrow_invocable.
3. Core Idea
For assignment in C++17, the right operand is sequenced before the left operand. Separately, a pointer type may explicitly require a non-throwing function, allowing compile-time distinction between potentially throwing and noexcept targets.
Keep the type, object lifetime, ownership, and evaluation boundary visible while reading the example; syntax is useful only when those semantics are understood.
4. Minimal Syntax
using Safe = void (*)() noexcept;
Safe action = &safe_action;
values[index()] = produce();5. How It Works
Logging helpers expose the evaluation of an assignment's right side before the indexed left side.
A
noexceptfunction pointer and type trait then verify the non-throwing callable contract.The program prints
valuebeforeindex, then the stored number and safe action text, giving a small test oracle that can be compared with the prediction made before compilation.
6. Common Mistakes
C++17 does not impose a simple left-to-right rule on all function arguments; avoid relying on order unless a specific sequencing rule guarantees it.
A successful build is not proof of correct semantics. Recheck lifetimes, invalidation, ordering, error paths, and required headers or link flags for the real program.
7. When to Use It
Use this technique when reviewing expressions with side effects or encoding a non-throwing callback contract in a type.
Choose a simpler C++11/14 form when the C++17 rule does not improve safety, clarity, or measured performance for the supported toolchains.
8. Simple Example
The assignment uses separate logging functions so order is visible. A static assertion confirms that the selected function pointer is nothrow-invocable.
The companion .cpp file has no input or external dependency. Predict the complete output, compile it, run it, then change one constant and explain the new result.
Complete sample code
Source file
cpp17/22_evaluation_order_noexcept_function_type/main.cpp
#include <array>
#include <iostream>
#include <type_traits>
int index() {
std::cout << "index\n";
return 0;
}
int produce() {
std::cout << "value\n";
return 42;
}
void safe_action() noexcept {
std::cout << "safe action\n";
}
int main() {
std::array<int, 1> values{};
values[index()] = produce(); // right operand is sequenced first
using Safe = void (*)() noexcept;
Safe action = &safe_action;
static_assert(std::is_nothrow_invocable_v<Safe>);
std::cout << "stored: " << values[0] << '\n';
action();
}
9. Key Takeaways
Reason from exact sequencing rules, and treat
noexceptas part of callable type compatibility in C++17.C++17 mode must be selected explicitly; a newer compiler default can otherwise hide a portability error.
Warnings, deterministic examples, and small assertions turn a remembered rule into evidence.
Document any lifetime, ownership, synchronization, or allocation contract at the API boundary.
10. Self-Check Questions
Easy — What problem does Evaluation Order and noexcept in Function Types address?
Medium — Which logging word appears first in the assignment expression?
Hard — Which conversions are allowed between throwing and non-throwing function pointers?