There is a well-known trick to cause a compile-time error in the evaluation of a constexpr
function by doing something like this:
constexpr int f(int x) { return (x != 0) ? x : throw std::logic_error("Oh no!"); }
And if the function is used in a constexpr
context you will get a compile-time error if x == 0
. If the argument to f
is not constexpr
, however, then it will throw an exception at run time if x == 0
, which may not always be desired for performance reasons.
Similar to the theory of assert
being guarded by NDEBUG
, is there a way to cause a compile-time error with a constexpr
function, but not do anything at run time?
Finally, do relaxed constexpr
rules in C++1y (C++14) change anything?
constexpr functions will be evaluated at compile time when all its arguments are constant expressions and the result is used in a constant expression as well.
Quick A: constexpr guarantees compile-time evaluation is possible if operating on a compile-time value, and that compile-time evaluation will happen if a compile-time result is needed.
The easiest way to check whether a function (e.g., foo ) is constexpr is to assign its return value to a constexpr as below: constexpr auto i = foo(); if the returned value is not constexpr compilation will fail.
Even though try blocks and inline assembly are allowed in constexpr functions, throwing exceptions or executing the assembly is still disallowed in a constant expression.
Is there a way to cause a compile-time error with a constexpr
function, but not do anything at run time?
You can use the exact same trick, but instead of using a throw-expression, use an expression that is not a constant expression but does what you want at runtime. For instance:
int runtime_fallback(int x) { return x; } // note, not constexpr constexpr int f(int x) { return (x != 0) ? x : runtime_fallback(0); } constexpr int k1 = f(1); // ok constexpr int k2 = f(0); // error, can't call 'runtime_fallback' in constant expression int k3 = f(0); // ok
Do relaxed constexpr
rules in C++1y (C++14) change anything?
Not in this area, no. There are some forms of expression that are valid in constant expressions in C++14 but not in C++11, but neither throw-expressions nor calls to non-constexpr
functions are on that list.
If the argument to f is not
constexpr
, however, then it will throw an exception at run time ifx == 0
, which may not always be desired for performance reasons.
A function argument is never considered to be a constant expression. The distinction would require compile-time and runtime objects to have different types.
Even though the compiler is using pure functional semantics when it evaluates the function at compile time, it's still the same function with the same meaning. If you want another function of similar but different meaning, you will have to either define another entire function, or make a template.
You could use a signature like this:
template< typename int_type > constexpr int f(int_type x);
with calls like this:
f( std::integral_constant< int, 0 >() ) // Error. f( std::integral_constant< int, 3 >() ) // OK. f( 0 ) // Not checked.
Metaprogramming can tell that integral_constant
means a compile-time value. But I don't think it's really appropriate. If one sense of the function works with zero and the other doesn't, then you have two different functions.
A wrapper idiom could prevent duplication among the different functions:
constexpr int f_impl(int x) { // Actual guts of the function. return x; } int f(int x) { // Non-constexpr wrapper prevents accidental compile-time use. assert ( x != 0 && "Zero not allowed!" ); return f_impl( x ); } template< int x > // This overload handles explicitly compile-time values. constexpr int f( std::integral_constant< int, x > ) { static_assert ( x != 0, "Zero not allowed!" ); return f_impl( x ); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With