在`static_assert`,`if constexpr(...)`和`constexpr`变量之间的模板中对`constexpr` lambdas的评估不一致

时间:2016-10-27 11:04:19

标签: c++ lambda constexpr c++17 constant-expression

(使用g++ 7.0主干。)

鉴于以下“类型到价值包装”实用程序......

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_assertconstexpr auto x = /*...*/不一致。

这是g++ if constexpr的实施中的缺陷吗?

Full example on wandbox

2 个答案:

答案 0 :(得分:1)

不一致的行为报告为bug #78131

答案 1 :(得分:0)

从我可以收集的内容来看,lambda本身不是constexpr。 你可以做到

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

然后

if constexpr(is_valid(f)(type_c<T0>, type_c<T1>)) { }
相关问题