2015-12-16 24 views
6

我似乎無法理解爲什麼有型const int的下面的代碼編譯:爲什麼類型const double沒有被達到範圍的lambda捕獲,但const int是?

int main() 
{ 
    using T = int; 
    const T x = 1; 
    auto lam = [] (T p) { return x+p; }; 
} 
$ clang++ -c lambda1.cpp -std=c++11 
$ 

,而這其中有const類型雙不:

int main() 
{ 
    using T = double; 
    const T x = 1.0; 
    auto lam = [] (T p) { return x+p; }; 
} 
$ clang++ -c lambda2.cpp -std=c++11 
lambda1.cpp:5:32: error: variable 'x' cannot be implicitly captured in a lambda with no capture-default specified 
    auto lam = [] (T p) { return x+p; }; 
          ^
lambda1.cpp:4:11: note: 'x' declared here 
    const T x = 1.0; 
     ^
lambda1.cpp:5:14: note: lambda expression begins here 
    auto lam = [] (T p) { return x+p; }; 
      ^
1 error generated. 

與constexpr雙尚未編譯:

int main() 
{ 
    using T = double; 
    constexpr T x = 1.0; 
    auto lam = [] (T p) { return x+p; }; 
} 
$ clang++ -c lambda3.cpp -std=c++11 
$ 

爲什麼int的行爲不同於double,或者除int以外的任何其他類型,即int被const限定符接受,但double/other類型s必須是constexpr?另外,爲什麼這個代碼用C++ 11編譯,我從[1]中得到的理解是,這種隱式捕獲是C++ 14的特性。

.. [1] how is this lambda with an empty capture list able to refer to reaching-scope name?

+0

關聯[靜態類成員類型爲double的常量表達式初始值設定項](http://stackoverflow.com/q/30742288/1708801) –

+0

我從來沒有理解爲什麼這麼多問題不包括未版本化的語言標記。你在這個問題上得到了更多的關注,不是每個跟蹤C++標籤的人都跟蹤C++ 11或C++ 14等...... –

+0

感謝您的反饋和您的詳細解答。我將確保C++標籤在未來的發佈中進入問題。 – eg0x20

回答

2

這樣做的結果是保持C++ 03兼容性,因爲在C++ 03中const const或const枚舉類型用常量表達式初始化,可以在常量表達式中使用,但這不適用於浮動點。

保持限制的理由可以在C++ 11(這說明了ABI破評論),並要求(重點煤礦),之後來到defect report 1826發現:

一個const整數用常量初始化可用於常量表達式,但用常量初始化的常量浮點變量不能。這個是故意的,爲了與C++ 03兼容,同時鼓勵一致使用constexpr。然而,有些人發現這種區別令人驚訝。

也有人觀察到,允許const浮點變量作爲常量表達式將是ABI突破性的變化,因爲它會影響lambda捕獲。

一種可能性可能是不推薦在常量表達式中使用const整型變量。

和反應是:

CWG認爲,目前的規則不應該改變,程序員渴望浮點值參加常量表達式應該使用常量的constexpr。

我們可以注意到,這個問題指出,允許常量浮點變量是常量表達式將是一個ABI斷相對於拉姆達捕獲。

由於lambda不需要捕獲變量(如果它不是odr使用的並且允許const浮點變量是常量表達式將允許它們落入該異常下),情況就是如此。

這是因爲在常量表達式中允許使用常量表達式或constexpr文字類型初始化的常量整數或枚舉類型的左值到右值轉換。對於使用常量表達式初始化的常量浮點類型,不存在此類異常。這是覆蓋在草案C++ 11標準部5.19

一個條件表達式是一個核心常量表達式除非它涉及以下爲潛在 評估子表達式中的一個[...]

,幷包括:

  • 左值到右值轉換(4。1),除非它被施加到
    • 其指的是與前面的初始化非易失性const對象整型或枚舉類型的glvalue,初始化 具有恆定表達,或
    • 文字類型的glvalue那指的是與constexpr定義的非揮發性物體,或者是指這樣的 對象的一個​​子對象,或

改變,這將潛在地影響的大小一個lambda對象(如果他們不再需要捕獲非ABR中斷的非odr使用的常量浮點值)。這個限制最初是爲了保持C++ 03的兼容性並鼓勵使用constexpr,但現在這個限制已經到位了,它很難將其移除。

請注意,在C++ 03中,我們只允許爲const整型或常量枚舉類型指定類內常量初始值設定項。在C++ 11中,這被擴展了,我們被允許使用括號或等於初始值設定項爲constexpr文字類型指定常量初始值設定項。

6

根據標準§5.1.2/ P12 Lambda表達式[expr.prim.lambda](重點礦山):

甲λ-表達與關聯的捕獲 - 默認值 明確捕獲this或具有自動存儲持續時間的變量 (這不包括任何已發現指代初始捕獲的關聯非靜態數據成員的id-表達式),稱爲 隱式捕捉實體(即,this或一個變量)如果 化合物語句:

(12.1) - ODR用途(3.2)的實體,或

(12.2) - 名稱在一個潛在的評估表達式(3.2),其中 包圍全表達實體依賴於λ-表達 的到達範圍內聲明一個通用的拉姆達參數 [實施例:

void f(int, const int (&)[2] = {}) { } // #1 
void f(const int&, const int (&)[1]) { } // #2 
void test() { 
const int x = 17; 
auto g = [](auto a) { 
f(x); // OK: calls #1, does not capture x 
}; 
auto g2 = [=](auto a) { 
int selector[sizeof(a) == 1 ? 1 : 2]{}; 
f(x, selector); // OK: is a dependent expression, so captures x 
}; 
} 

- 結束示例]所有這些隱式捕獲的實體應該在lambda表達式的範圍內聲明的 。 [注意: 由嵌套的lambda表達式隱式捕獲實體可以通過包含的lambda表達式(參見下面的 )導致它的隱式捕獲 。隱式odr使用這可能導致隱式捕獲。 - 結束說明]

這裏的標準規定了什麼是lambda表達式中的變量需要被捕獲,如果它是odr使用的。通過odr使用標準意味着需要變量定義,或者是因爲它的地址被採用或者有一個對它的引用。

但是這條規則有例外。

變量X,其名稱顯示爲潛在的:其中一個特別感興趣的標準§3.2/ P3之一定義規則[basic.def.odr](重點礦山)被發現 - 除非應用左值到右值轉換 (4。1)到x得到一個常數表達式(5.20),它不會調用任何不平凡的函數,如果x是一個對象,ex是一個元素 表達式e的潛在結果的集合,...

現在,如果在示例:

int main() { 
    using T = int; 
    const T x = 1; 
    auto lam = [] (T p) { return x+p; }; 
} 

int main() { 
    using T = double; 
    constexpr T x = 1.0; 
    auto lam = [] (T p) { return x+p; }; 
} 

申請一個左到右值的轉換上x我們得到了一個CONSTA nt表達式,因爲在第一個例子中,x是一個積分常數,在第二個例子中,x被聲明爲constexpr。因此,這些上下文中不需要捕獲x

然而,這是不是對的情況爲例:在這個例子中

int main() { 
    using T = double; 
    const T x = 1.0; 
    auto lam = [] (T p) { return x+p; }; 
} 

如果我們將左值到右值轉換爲x我們沒有得到一個常量表達式。

現在你可能想知道爲什麼這種情況,因爲xconst double。那麼答案是,如果一個變量是常量積分或枚舉類型,並且在聲明時使用常量表達式進行初始化,那麼在沒有constexpr的情況下聲明的變量將被限定爲常量表達式。這是通過標準的在§5.20/ p2.7.1正當常量表達式[expr.const](重點礦山):

甲條件表達式e是一個核心常量表達式除非e的 評價,以下抽象機(1.9), 將評估下列用語的一個規則:

...

(2.7) - 除非它被應用到一個左到右值轉換(4.1)

(2.7.1) - 積分或枚舉類型 指的是與前述 初始化一個完整的非易失性const對象,以恆定表達,初始化非易失性glvalue ...

因此,需要捕獲const double變量,因爲左值到右值轉換不會喊出常量表達式。因此,你會得到一個編譯器錯誤。

相關問題