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.