Ниже приведены три попытки реализовать is_constexpr() на основе отвечатьРичард Смит до Возможен ли is_constexpr в С++ 11?.
Версия 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); }
Версия 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); }
Версия 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); }
Я протестировал вышеизложенное (см. божественная стрела) с gcc 9.1, clang 8.0.0, icc 19.0.1 и msvc 19.20 и помощью следующих функций:
void constexpr f_c(int) {}
void f_nc(int) {}
В таблице ниже показаны выражения, которые я поставил в static_assert. Я бы ожидал, что все они пройдут, но компиляторы не согласны со мной и между собой (кроме icc и msvc, которые согласуются друг с другом):
| 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 |
Кто прав и почему? (Цитаты из Стандарта были бы полезны.)





Ни is_constexpr_1, ни is_constexpr_2 недействительны, потому что они противоречат обычному правилу, согласно которому параметры функции нельзя использовать в константных выражениях. Они требуют, чтобы x и f, соответственно, хотя бы иногда можно было использовать в качестве постоянного выражения, а это никогда не так.
В этом случае ограничение [expr.const]/4:
an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either [...]
Неважно, какие две другие пули, так как у нас нет предшествующей инициализации в нашей id-выражение, которая ссылается на переменную.
is_constexpr_3 действителен, как объясняет Ричард Смит в связанный ответ.
Мои ожидания:
|
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
что делает clang.