Is it legal to use side-effects in exceptions thrown by constexpr?
assert, c++, c++11, constexpr, language-lawyer
Solution
It is legal.
For each `constexpr` function there must be some argument values that result in a constant expression (§7.1.5/5):
For a `constexpr` function, if no function argument values exist such that the function invocation substitution would produce a constant expression (5.19), the program is ill-formed; no diagnostic required.
Note that this does not mean that every possible argument value must result in a constant expression. `divide` clearly has some argument values that result in a constant expression: `divide(1, 1)` is a simple example. So, the definition is clearly valid.
But can `divide(1, 0)` be called? Yes, it can. There's almost no difference between invoking a `constexpr` function or a "normal" function (§7.1.5/7):
A call to a `constexpr` function produces the same result as a call to an equivalent non-`constexpr` function in all respects except that a call to a `constexpr` function can appear in a constant expression.
Note that calls to `constexpr` functions can appear in constant expressions, but nothing forbids them from not resulting in constant expressions. This is intended so one can call `constexpr` functions with both compile-time and runtime arguments (otherwise usefulness of `constexpr` would be severaly limited).
For completeness, let's see what makes a constant expression (§5.19/2):
A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (§3.2), but subexpressions of logical AND (§5.14), logical OR (§5.15), and conditional (§5.16) operations that are not evaluated are not considered [...].
So, `divide(1, 1)` is a constant expression, but `divide(1, 0)` is not. If you used `divide(1, 0)` in a template parameter, the program would be ill-formed. But otherwise it's fine.
Problem
Normally, constexpr must be free of side-effects. However, I just discovered that it is possible to use side-effects in the constructors of thrown exceptions. That technique can be used to emulate assert() for constexpr functions, as it is demonstrated in the following program. ``` #include <iostream> #include <cstdlib> #include <stdexcept> struct constexpr_precond_violated : std::logic_error { constexpr_precond_violated(const char* msg) : std::logic_error(msg) { std::cerr << msg << '\n'; abort(); // to get a core dump } }; #define TO_STRING_IMPL(x) #x #define TO_STRING(x) TO_STRING_IMPL(x) #define CONSTEXPR_PRECOND(cond, value) \ ((!(cond)) ? throw constexpr_precond_violated( \ "assertion: <" #cond "> failed (file: " \ __FILE__ ", line: " TO_STRING(__LINE__) ")") \ : (value)) constexpr int divide(int x, int y) { return CONSTEXPR_PRECOND(y != 0, x / y); } int main(int argc, char** argv) { // The compiler cannot know argc, so it must be evaluated at runtime. // If argc is 2, the precondition is violated. return divide(100, argc - 2); } ``` I tested it with g++ 4.7.2 and clang++ 3.1. When the preconditions fails, you get the error location and a core dump. ``` ./constexpr_assert some_arg assertion: <y != 0> failed (file: constexpr_assert.cpp, line: 26) Aborted (core dumped) ``` So it works with the current compilers, but is it legal C++11?