簡體   English   中英

GCC和Clang不同意C ++ 17 constexpr lambda捕獲

[英]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。

[expr.const] /2.12

表達式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>對象,但問題是標准中沒有任何內容表示相反 - 所有復制初始化來自iintegral_constant<int, 42>對象計為l-to-r轉換。

暫無
暫無

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

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