GCC和Clang关于lambda的可解释性不同意见吗?

时间:2019-06-11 05:22:23

标签: c++ gcc clang metaprogramming constexpr

为什么Clang不能编译以下代码,并显示表达式不是constexpr的消息,为什么GCC不能编译?哪个编译器正确? https://godbolt.org/z/nUhszh (显然,这只是一个例子。事实上,我确实需要能够在constexpr上下文中调用constexpr函数对象。)

#include <type_traits>

template <typename Predicate>
constexpr int f(Predicate&& pred) {
    if constexpr (pred(true)) {
        return 1;
    } 
    else {
        return 0;
    }
}

int main() {
    f([](auto m) {
        return std::is_same_v<decltype(m), bool>;
    });
}

使用-std=c++17 -stdlib=libc++ -O1 -march=skylake输出clang 8.0.0:

<source>:5:19: error: constexpr if condition is not a constant expression

    if constexpr (pred(true)) {

                  ^

<source>:14:5: note: in instantiation of function template specialization 'f<(lambda at <source>:14:7)>' requested here

    f([](auto m) {

    ^

1 error generated.

Compiler returned: 1

2 个答案:

答案 0 :(得分:3)

通过在if constexpr中使用谓词,您希望它是无条件的常量表达式。但是constexpr函数可以总是在非constexpr上下文中使用非常量表达式的参数来调用。因此,函数参数可能从不假定为常量表达式。

因此,GCC接受未经修改的代码是错误的。

碰巧的是,您的特定示例不需要if constexpr即可在constexpr上下文中工作。修改后的功能:

template <typename Predicate>
constexpr int f(Predicate&& pred) {
    if (pred(true)) {
        return 1;
    } 
    else {
        return 0;
    }
}

在需要常量表达式时可由both compilers调用:

int main() {
    constexpr int i = f([](auto m) constexpr {
        return std::is_same_v<decltype(m), bool>;
    });
    return i;
}

答案 1 :(得分:2)

C语是正确的。在表达式pred(true)中, id-expression pred表示引用类型的变量。只有引用类型的变量由常量表达式初始化,或者在对表达式([expr.const]/2.11)求值时进行初始化,引用类型的变量才能出现在常量表达式中。

所以pred(true)不是常量表达式。

如果将参数pred的声明更改为Predicate pred,则Pred将不是引用类型。表达式pred(true)等效于pred.operator()(true)pred将是class member access expression中的 object-expression ,因此,不会将{em>左值到右值转换应用于{{ 1}}。因此, id-expression pred不必通过常量表达式初始化(请参见[expr.const]/2.7.2)即可成为常数表达式。然后,函数调用是常量表达式,因为调用运算符隐式是constexpr函数[expr.prim.lambda.closure]/4

这些事实证明@NikosC的建议是正确的。将pred声明为Pred。在这种情况下,您的代码将同时使用Clang和Gcc进行编译,并且符合标准。