`static_assert`、`if constexpr(...)` 和 `constexpr` 变量之间模板中 `constexpr` lambda 的计算不一致
Inconsistent evaluation for `constexpr` lambdas in templates between `static_assert`, `if constexpr(...)` and `constexpr` variables
(使用g++ 7.0
中继。)
鉴于以下 "type-to-value wrapping" 实用程序...
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{};
...我创建了以下函数来检查表达式的有效性:
template <typename TF>
constexpr auto is_valid(TF)
{
return [](auto... ts) constexpr
{
return std::is_callable<TF(typename decltype(ts)::type...)>{};
};
}
is_valid
函数可以如下使用:
// 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>);
里面也可以用static_assert
...
static_assert(is_valid([](auto _0) constexpr -> decltype(_0.hello()){})
(type_c<A>));
...并在 if constexpr
内:
if constexpr(
is_valid([](auto _0) constexpr -> decltype(_0.hello()){})
(type_c<A>)) { /* ... */ }
但是,当在模板函数 中使用 is_valid
(将模板参数作为 type_c
值传递) 时,会发生一些奇怪的事情:
static_assert(is_valid(/*...*/))
正常工作。
constexpr auto x = is_valid(/*...*/)
正常工作。
if constexpr(is_valid(/*...*/)
编译失败.
// 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>)) { }
}
错误:
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>)) { }
为什么这仅针对 if constexpr(is_valid(/*...*/))
情况编译失败? 这与 static_assert
和 constexpr auto x = /*...*/
不一致。
这是 g++
实现 if constexpr
的缺陷吗?
据我所知,lambda 本身不是 constexpr
。
你可以做到
constexpr auto f = [](auto _0, auto _1) -> decltype(_0 + _1){};
然后
if constexpr(is_valid(f)(type_c<T0>, type_c<T1>)) { }
不一致的行为被报告为 bug #78131。
(使用g++ 7.0
中继。)
鉴于以下 "type-to-value wrapping" 实用程序...
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{};
...我创建了以下函数来检查表达式的有效性:
template <typename TF>
constexpr auto is_valid(TF)
{
return [](auto... ts) constexpr
{
return std::is_callable<TF(typename decltype(ts)::type...)>{};
};
}
is_valid
函数可以如下使用:
// 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>);
里面也可以用static_assert
...
static_assert(is_valid([](auto _0) constexpr -> decltype(_0.hello()){})
(type_c<A>));
...并在 if constexpr
内:
if constexpr(
is_valid([](auto _0) constexpr -> decltype(_0.hello()){})
(type_c<A>)) { /* ... */ }
但是,当在模板函数 中使用 is_valid
(将模板参数作为 type_c
值传递) 时,会发生一些奇怪的事情:
static_assert(is_valid(/*...*/))
正常工作。constexpr auto x = is_valid(/*...*/)
正常工作。if constexpr(is_valid(/*...*/)
编译失败.
// 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>)) { }
}
错误:
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>)) { }
为什么这仅针对 if constexpr(is_valid(/*...*/))
情况编译失败? 这与 static_assert
和 constexpr auto x = /*...*/
不一致。
这是 g++
实现 if constexpr
的缺陷吗?
据我所知,lambda 本身不是 constexpr
。
你可以做到
constexpr auto f = [](auto _0, auto _1) -> decltype(_0 + _1){};
然后
if constexpr(is_valid(f)(type_c<T0>, type_c<T1>)) { }
不一致的行为被报告为 bug #78131。