[英]GCC and Clang disagree about C++17 constexpr lambda captures
[英]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的提議是正確的。 宣布Pred
為Predicate Pred
。 在這種情況下,您的代碼將使用Clang和Gcc進行編譯,並且將符合標准。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.