c++ - GCC 和 Clang 不同意 lambda 的 constexpr-ness?

标签 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

最佳答案

Clang 是对的。在表达式 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 中的一个对象表达式 ,因此,lvalue-to-rvalue 转换不会应用于 pred。因此,id-expression pred 不必通过常量表达式(参见 [expr.const]/2.7.2 )来初始化为 常量表达式。然后函数调用是一个常量表达式,因为调用运算符隐式是一个constexpr 函数[expr.prim.lambda.closure]/4 .

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

关于c++ - GCC 和 Clang 不同意 lambda 的 constexpr-ness?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56536970/

相关文章:

macos - 构建 ELLCC 时出错

c++ - VIM 中 c++ 类标签的确定性导航。 ctags 效果不佳

c++ - 我的日志记录模块线程安全吗?

c++ - 编写将使用静态库的动态库时出错

c++ - 使用斐波那契递归打印 1 到 n

c++ - GCC 对 C++17 的支持情况如何?

java - 无法使用autocmd设置的gcc在MacVim中编译java

c - Makefile 无法在子文件夹中找到文件

c++ - 通用方法指针。指向不同类的 reinterpret_cast 方法指针,这是 UB 吗?

c++ - 使用 clang 编译时出现正则表达式段错误,可能是编译器错误?