[英]GCC and Clang disagree about C++17 constexpr lambda captures
考慮這個將變量聲明為constexpr的示例,通過lambda中的副本捕獲它,並聲明另一個constexpr變量,該變量是constexpr函數從原始變量展開非類型模板參數的結果。
#include <utility>
template<int I>
constexpr auto unwrap(std::integral_constant<int, I>) {
return I;
}
int main() {
constexpr auto i = std::integral_constant<int, 42>{};
constexpr auto l = [i]() {
constexpr int x = unwrap(i);
};
}
Clang(trunk)接受此代碼。 ( wandbox )
GCC(主干)失敗,出現以下錯誤消息( wandbox ):
lambda_capture.cpp:11:31: error: the value of ‘i’ is not usable in a constant expression
constexpr int x = unwrap(i);
^
lambda_capture.cpp:10:28: note: ‘i’ was not declared ‘constexpr’
constexpr auto l = [i]() {
哪個編譯器正確? 在我看來,這是一個GCC錯誤,其中lambda捕獲的constexpr-ness沒有正確傳播到lambda上下文。
兩個實現都被竊聽,但我傾向於認為GCC在這里得到了正確的答案。
刪除i
的捕獲導致Clang拒絕編譯代碼。 這意味着它顯然在某個地方有一個bug。
表達式
e
是核心常量表達式,除非根據抽象機器的規則評估e
將評估以下表達式之一:
- [...]
- 在lambda表達式中 ,對一個變量的引用,該變量具有在lambda表達式之外定義的自動存儲持續時間,其中引用將是odr-use;
- [...]
Clang的行為是精神分裂的:如果在身體中使用i
不是一種使用,那么就不需要捕獲它,但如果刪除了顯式捕獲,它會拒絕OP中的代碼; OTOH,如果是odr-use,那么通過上面的unwrap(i)
不是常量表達式,因此它應該拒絕x
的初始化。
GCC的lambda實現在使用odr方面非常糟糕。 它確實超早期折疊,導致各種微妙的惡作劇。 另一方面,對於顯式捕獲,它轉換所有用途,無論它是否實際上是一種使用。 積極的常量折疊意味着如果刪除i
的捕獲,它接受OP的代碼。
假設unwrap(i)
不ODR使用i
,那么它是正確的,每[expr.const] /2.12,OP的代碼是形成不良的。
不unwrap(i)
其實ODR利用i
? 這個問題歸結為復制初始化unwrap
的參數對象是否計算為對i
應用左值到右值的轉換。 我沒有在標准中看到任何明確表示在這里應用左值到右值的轉換,而[dcl.init] /17.6.2表示我們調用了一個構造函數(在這種情況下,隱含地定義了一個簡單的復制構造函數)將i
作為參數綁定到其參數,並且引用綁定是odr-use的典型示例。
可以肯定的是,應用l-to-r轉換會導致從i
復制初始化一個integral_constant<int, 42>
對象,但問題是標准中沒有任何內容表示相反 - 所有復制初始化來自i
的integral_constant<int, 42>
對象計為l-to-r轉換。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.