從沒有捕獲的 C 20 閉包型別開始具有默認建構式,請參閱https://en.cppreference.com/w/cpp/language/lambda:
如果未指定捕獲,則閉包型別具有默認的默認建構式。
但是捕獲型別的閉包如何構造它們的物件呢?
一種方法是使用std::bit_cast(前提是閉包型別可以簡單地復制)。Visual Studio 編譯器為閉包型別提供了一個建構式,如示例所示:
#include <bit>
int main() {
int x = 0;
using A = decltype([x](){ return x; });
// ok everywhere
constexpr A a = std::bit_cast<A>(1);
static_assert( a() == 1 );
// ok in MSVC
constexpr A b(1);
static_assert( b() == 1 );
}
演示:https : //gcc.godbolt.org/z/dnPjWdYx1
考慮到 Clang 和 GCC 都拒絕A b(1),標準不需要這個建構式的存在。但是編譯器可以提供這樣的建構式作為擴展嗎?
uj5u.com熱心網友回復:
但是捕獲型別的閉包如何構造它們的物件呢?
你不能。它們只能從 lambda 運算式創建。
不,bit_cast不是“無處不在”。C 標準中沒有任何規則要求任何特定的 lambda 型別必須是可簡單復制的(或者與其捕獲成員的大小相同)。當前沒有實作破壞您的代碼的事實并不意味著未來的實作不能。
如果您有多個捕獲成員,它肯定不會起作用。
停止將 lambda 視為創建型別的廉價方式。如果要使用可以構造的成員創建可呼叫型別,請執行以下操作:
#include <bit>
int main() {
struct A
{
int x = 0;
constexpr auto operator() {return x;}
};
// ok everywhere
constexpr A b(1);
static_assert( b() == 1 );
}
uj5u.com熱心網友回復:
由于這是標記為language-lawyer,這就是 C 標準對這一切的看法。
但是捕獲型別的閉包如何構造它們的物件呢?
cppreference 鏈接參考的標準的實際部分是[expr.prim.lambda.general] - 7.5.5.1.14:
如果 lambda 運算式具有 lambda 捕獲和默認的默認建構式,則與 lambda 運算式關聯的閉包型別沒有默認建構式。它有一個默認的復制建構式和一個默認的移動建構式([class.copy.ctor])。如果 lambda 運算式具有 lambda 捕獲和默認的復制和移動賦值運算子,則它具有已洗掉的復制賦值運算子([class.copy.assign])。
但是,第1 條和第2 條說:
型別的λ表達的(也是封閉的物件的型別)是一個獨特的,無名不愈合型別,被稱為閉合型別,其屬性被描述如下。
閉包型別不是聚合型別。一個實作可以定義與下面描述的不同的閉包型別,前提是這不會改變程式的可觀察行為,而不是改變:[無關的東西]
這意味著(除了不相關的例外),所描述的 lambda 介面是詳盡無遺的。在明確列出的內容之上的任何其他可訪問成員都必須是擴展名。由于除了默認的建構式之外沒有列出其他建構式,因此應該是唯一的建構式。
注意:一個 lambda 可能相當于一個基于類的函子,但它不是純粹的語法糖。編譯器/實作不需要建構式來構造和引數化 lambda 的型別。只是程式員因為缺少建構式而無法創建實體。
就擴展而言:
但是編譯器可以提供這樣的建構式作為擴展嗎?
是的。允許編譯器將此功能作為擴展提供,只要它所做的只是使程式的格式不正確即可。
來自[intro.compliance.general] - 4.1.1.8:
一個符合要求的實作可以有擴展(包括額外的庫函式),前提是它們不會改變任何格式良好的程式的行為。需要實作來診斷使用根據本檔案格式錯誤的此類擴展的程式。但是,這樣做之后,他們就可以編譯和執行此類程式。
但是,對于手頭的功能,MSVC 在作為擴展的實作中會遇到問題:
- 它應該發出診斷資訊。
- 根據它自己的檔案,它在使用時應該拒絕代碼
/permissive-。然而事實并非如此。
所以看起來 MSVC 有意或無意地表現得好像這是語言的一部分,但事實并非如此。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/331737.html
上一篇:在vs2019中創建服務
下一篇:C 2D演算法代碼讀取陣列錯誤
