The following are three attempts to implement is_constexpr()
based on a Richard Smith's answer to Is is_constexpr possible in C++11?
Version 1
template <typename T>
bool constexpr is_constexpr_impl_1(const T& x, decltype(int{(x, 0u)})) { return true; }
template <typename T>
bool constexpr is_constexpr_impl_1(const T&, ...) { return false; }
template <typename T>
bool constexpr is_constexpr_1(const T& x) { return is_constexpr_impl_1(x, 0); }
Version 2
template <typename T>
bool constexpr is_constexpr_impl_2(const T& f, decltype(int{(f(0), 0u)})) { return true; }
template <typename T>
bool constexpr is_constexpr_impl_2(const T&, ...) { return false; }
template <typename T>
bool constexpr is_constexpr_2(const T& f) { return is_constexpr_impl_2(f, 0); }
Version 3
template <auto f>
bool constexpr is_constexpr_impl_3(decltype(int{(f(0), 0u)})) { return true; }
template <auto f>
bool constexpr is_constexpr_impl_3(...) { return false; }
template <auto f>
bool constexpr is_constexpr_3() { return is_constexpr_impl_3<f>(0); }
I've tested the above (see in godbolt) with gcc 9.1, clang 8.0.0, icc 19.0.1 and msvc 19.20 and the help of the following functions:
void constexpr f_c(int) {}
void f_nc(int) {}
Table below shows the expressions I've put in a static_assert
. I would expect all of them to pass but compilers disagree with me and between themselves (except icc and msvc which agree with one another):
| gcc | clang | icc | msvc |
is_constexpr_1(0) | pass | fail | pass | pass |
is_constexpr_2(f_c) | fail | fail | pass | pass |
!is_constexpr_2(f_nc) | pass | pass | fail | fail |
is_constexpr_3<f_c>() | pass | pass | pass | pass |
!is_constexpr_3<f_nc>() | pass | pass | fail | fail |
Who is right and why? (Quotes from the Standard would be useful.)