首页 > 解决方案 > GCC 和 Clang 不同意 lambda 的 constexpr-ness?

问题描述

为什么 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>;
    });
}

clang 8.0.0 的输出-std=c++17 -stdlib=libc++ -O1 -march=skylake

<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

标签: c++gccclangmetaprogrammingconstexpr

解决方案


By using the predicate in an if constexpr you expect it to be unconditionally a constant expression. But a constexpr function can always be called in a non-constexpr context with arguments that are not constant expressions. So a function parameter may never be assumed to be a constant expression.

GCC is therefore wrong to accept your code unmodified.

It just so happens that your specific example doesn't require an if constexpr to work in a constexpr context. A modified function:

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

Is invocable by both compilers when a constant expression is required:

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

推荐阅读