I recently asked a question here (Detecting instance method constexpr with SFINAE) where I tried to do some constexpr detection at compile time. Eventually, I figured out that one can exploit noexcept
to do this: any constant expression is also noexcept
. So I put together the following machinery:
template <class T>
constexpr int maybe_noexcept(T && t) { return 0; }
...
constexpr bool b = noexcept(maybe_noexcept(int{}));
This works and b
is true as you'd expect, as zero-initializing an int
is a constant expression. It also correctly yields zero when it should (if I change int
to some other appropriate type).
Next, I wanted to check if something is constexpr
move constructible. So I did this:
constexpr bool b = noexcept(maybe_noexcept(int(int{})));
And again, this works properly for int
, or a user defined type. However, this checks that the type has both a constexpr default constructor and a constexpr move constructor. So, to work around this, I tried to change to declval:
constexpr bool b = noexcept(maybe_noexcept(int(declval<int>())));
This results in b
being false in gcc 5.3.0 (can't use clang for any of this, because clang does not correctly make constant expressions noexcept
). No problem, I say, must be because declval
is (interestingly enough) not marked constexpr
. So I write my own naive version:
template <class T>
constexpr T&& constexpr_declval() noexcept;
Yes, this is naive compared to how the standard library does it as it will choke on void and probably other things, but it's fine for now. So I try again:
constexpr bool b = noexcept(maybe_noexcept(int(constexpr_declval<int>())));
This still does not work, b
is always false. Why is this not considered a constant expression? Is this a compiler bug, or am I not understanding fundamental about constexpr
? It seems like there is some strange interaction between constexpr
and unevaluated contexts.
The decltype type specifier yields the type of a specified expression. The decltype type specifier, together with the auto keyword, is useful primarily to developers who write template libraries. Use auto and decltype to declare a function template whose return type depends on the types of its template arguments.
constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.
constexpr stands for constant expression and is used to specify that a variable or function can be used in a constant expression, an expression that can be evaluated at compile time. The key point of constexpr is that it can be executed at compile time.
#define directives create macro substitution, while constexpr variables are special type of variables. They literally have nothing in common beside the fact that before constexpr (or even const ) variables were available, macros were sometimes used when currently constexpr variable can be used.
constexpr
expressions must be defined. Yours is not defined, so in that case int(constexpr_declval<int>())
is not constexpr
.
Which means maybe_noexcept(int(constexpr_declval<int>()))
is not a constexpr
, so is not noexcept
.
And the compiler properly returns false
.
You also cannot invoke UB in a constexpr
.
I cannot think of a way to make a constexpr
reference to arbitrary data. I was thinking a constexpr
buffer of aligned storage reinterpreted as a reference to the data type, but that is UB in many contexts, hence not-constexpr
.
In general, this isn't possible. Imagine you had a class whose state determines if the method call is constexpr
:
struct bob {
int alice;
constexpr bob(int a=0):alice(a) {}
constexpr int get() const {
if (alice > 0) throw std::string("nope");
return alice;
}
};
now, is bob::get
constexpr
or not? It is if you have a constexpr bob
constructed with a non-positive alice
, and ... it isn't if not.
You cannot say "pretend this value is constexpr
and tell me if some expression is constexpr
". Even if you could, it wouldn't solve the problem in general, because the state of a constexpr
parameter can change if an expression is constexpr
or not!
Even more fun, bob().get()
is constexpr, while bob(1).get()
is not. So your first attempt (default construct the type) even gave the wrong answer: you can test, then do the action, and the action will fail.
The object is effectively a parameter to the method, and without the state of al parameters, you cannot determine if a function is constexpr
.
The way to determine if an expression is constexpr
is to run it in a constexpr
context and see if it works.
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