簡體   English   中英

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

[英]GCC and Clang disagree about constexpr-ness of lambda?

為什么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

通過在if constexpr使用謂詞,您可以期望它無條件地成為常量表達式。 但是constexpr函數總是可以在非constexpr上下文中調用,其參數不是常量表達式。 因此,函數參數可能永遠不會被假定為常量表達式。

因此,GCC接受未經修改的代碼是錯誤的。

恰好,您的具體示例不需要if constexpr在constexpr上下文中工作。 修改后的功能:

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

當需要常量表達式時, 兩個編譯器都可以調用它:

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

Clang是對的。 在表達式pred(true)id表達式 pred表示引用類型的變量。 引用類型的變量只有在通過常量表達式初始化時或者在表達式( [expr.const] /2.11 )的求值期間執行初始化時才會出現在常量表達式中。

所以pred(true)不是一個常數表達式。

如果將參數pred的聲明更改為Predicate pred ,則Pred將不是引用類型。 表達式pred(true)將等同於pred.operator()(true) pred將是類成員訪問表達式中對象 表達式 ,因此,不會對pred應用左值到右值的轉換。 因此, id-expression pred不必通過常量表達式 (參見[expr.const] /2.7.2 )進行初始化,以便成為常量表達式 然后函數調用是一個常量表達式,因為調用操作符顯然是constexpr函數[expr.prim.lambda.closure] / 4

這些事實證明了@NikosC的提議是正確的。 宣布PredPredicate Pred 在這種情況下,您的代碼將使用Clang和Gcc進行編譯,並且將符合標准。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM