Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attempt to implement is_constexpr() - compilers diverge

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.)

like image 534
Cassio Neri Avatar asked May 21 '19 17:05

Cassio Neri


1 Answers

Neither is_constexpr_1 nor is_constexpr_2 are valid because they run afoul of the usual rule that function parameters are not usable in constant expressions. They require x and f, respectively, to be at least sometimes usable as a constant expression and they never are.

In this case, the [expr.const]/4 restriction of:

an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either [...]

Doesn't matter what the other two bullets are since we don't have preceding initialization on our id-expression that refers to a variable.

is_constexpr_3 is valid, as Richard Smith explains in the linked answer.

My expectation is:

                        | 
 is_constexpr_1(0)      | fail
 is_constexpr_2(f_c)    | fail
!is_constexpr_2(f_nc)   | pass
 is_constexpr_3<f_c>()  | pass
!is_constexpr_3<f_nc>() | pass

which is what clang does.

like image 70
Barry Avatar answered Sep 18 '22 23:09

Barry