Suppose I want to write a C++1y/14 constexpr function that performs integer square root:
constexpr int constexpr_isqrt(int x);
I want to perform a sanity check to make sure that x
is non-negative:
constexpr int constexpr_isqrt(int x)
{
if (x < 0)
???
...
}
What should I write in ???
above?
Ideally if the function is evaluated in constant context it should cause a compile-time error, and if called at run-time with a run-time error (eg abort or thrown exception).
A constexpr function is one whose return value is computable at compile time when consuming code requires it. Consuming code requires the return value at compile time to initialize a constexpr variable, or to provide a non-type template argument.
For example, the default constructor of std::unique_ptr is constexpr, allowing constant initialization. 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.
constexpr Containers and Algorithms of the Standard Template Library. 100 classical algorithms of the Standard Template Library are declared as constexpr . Consequently, you can sort a std::vector of ints at compile time.
A const int var can be dynamically set to a value at runtime and once it is set to that value, it can no longer be changed. A constexpr int var cannot be dynamically set at runtime, but rather, at compile time. And once it is set to that value, it can no longer be changed.
You're in luck, there is a way! Even in C++11! Use exceptions:
#include <iostream>
#include <stdexcept>
constexpr int foo(int a)
{
return (a >= 0) ? a : throw std::invalid_argument("Negative!");
}
template <int n>
struct Foo
{
};
int main()
{
Foo<foo(1)> f1(); // fine, guaranteed compile time
Foo<foo(-1)> f2(); // bad, compile time error
foo(1); // fine, not necessarily at compile time
try
{
foo(-1); // fine, definitively not at compile time
}
catch ( ... )
{
}
return 0;
}
Demo: http://ideone.com/EMxe2K
GCC has a rather good error message for the disallowed case:
prog.cpp: In function ‘int main()’:
prog.cpp:17:12: in constexpr expansion of ‘foo(-1)’
prog.cpp:6:63: error: expression ‘<throw-expression>’ is not a constant-expression
return (a >= 0) ? a : throw std::invalid_argument("Negative!");
For a C++1y constexpr function that looks like
constexpr foo(int a)
{
if ( a < 0 )
{
// error!
}
++a;
//something else
return a;
}
you can use the above pattern by introducing a new function:
constexpr foo_positive(int a)
{
++a;
//something else
return a;
}
constexpr int foo(int a)
{
return (a >= 0) ? foo_positive(a) : throw std::invalid_argument("Negative!");
}
Or you simply write
constexpr foo(int a)
{
if ( a < 0 )
{
throw std::invalid_argument("Negative!");
}
++a;
//something else
return a;
}
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