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.