Inconsistent evaluation of `constexpr` lambdas in templates between` static_assert`, `if constexpr (...)` and `constexpr`

(Using g++ 7.0 trunk.)

Based on the following value binding utilities ...

 template <typename T> struct type_wrapper { using type = T; }; // "Wraps" a type into a `constexpr` value. template <typename T> constexpr type_wrapper<T> type_c{}; 

... I created the following function that validates the expression:

 template <typename TF> constexpr auto is_valid(TF) { return [](auto... ts) constexpr { return std::is_callable<TF(typename decltype(ts)::type...)>{}; }; } 

The is_valid function can be used as follows:

 // Evaluates to `true` if `some_A.hello()` is a valid expression. constexpr auto can_add_int_and_float = is_valid([](auto _0) constexpr -> decltype(_0.hello()){}) (type_c<A>); // Evaluates to `true` if `some_int + some_float` is a valid expression. constexpr auto can_add_int_and_float = is_valid([](auto _0, auto _1) constexpr -> decltype(_0 + _1){}) (type_c<int>, type_c<float>); 

It can also be used inside static_assert ...

 static_assert(is_valid([](auto _0) constexpr -> decltype(_0.hello()){}) (type_c<A>)); 

... and inside if constexpr :

 if constexpr( is_valid([](auto _0) constexpr -> decltype(_0.hello()){}) (type_c<A>)) { /* ... */ } 

However, when is_valid used inside the template function (passing the template parameters as type_c values), something strange happens:

  • static_assert(is_valid(/*...*/)) works correctly.

  • constexpr auto x = is_valid(/*...*/) works correctly.

  • if constexpr(is_valid(/*...*/) cannot be compiled .

 // Compiles and works as intended. template <typename T0, typename T1> void sum_ok_0(T0, T1) { static_assert( is_valid([](auto _0, auto _1) constexpr -> decltype(_0 + _1){})(type_c<T0>, type_c<T1>) ); } // Compiles and works as intended. template <typename T0, typename T1> void sum_ok_1(T0, T1) { constexpr auto can_sum = is_valid([](auto _0, auto _1) constexpr -> decltype(_0 + _1){})(type_c<T0>, type_c<T1>); if constexpr(can_sum) { } } // Compile-time error! template <typename T0, typename T1> void sum_fail_0(T0, T1) { if constexpr(is_valid([](auto _0, auto _1) constexpr -> decltype(_0 + _1){})(type_c<T0>, type_c<T1>)) { } } 

Error:

 In function 'void sum_fail_0(T0, T1)': 64:95: error: expression '<lambda>' is not a constant expression if constexpr(is_valid([](auto _0, auto _1) constexpr -> decltype(_0 + _1){})(type_c<T0>, type_c<T1>)) { } 

Why can't this be compiled only for the case if constexpr(is_valid(/*...*/)) ? This is incompatible with static_assert and constexpr auto x = /*...*/ .

Is this a defect in the g++ implementation of if constexpr ?

Full example in wandbox .

+5
source share
2 answers

Inconsistent behavior has been reported as error # 78131 .

+1
source

From what I can assemble, lambda itself is not constexpr . You can do

 constexpr auto f = [](auto _0, auto _1) -> decltype(_0 + _1){}; 

And then

 if constexpr(is_valid(f)(type_c<T0>, type_c<T1>)) { } 
0
source

Source: https://habr.com/ru/post/1258885/


All Articles