[expr.prim.lambda.capture]/12:
如果物體被隱式或顯式捕獲但未通過副本捕獲,則通過參考捕獲物體。對于通過參考捕獲的物體,是否在閉包型別中宣告了其他未命名的非靜態資料成員,這是未指定的。如果宣告,此類非靜態資料成員應為文字型別。
閉包型別可以直接訪問物件,那么為什么有時需要參考成員呢?它甚至只要求成員是文字型別,為什么?
uj5u.com熱心網友回復:
這個措辭是在談論閉包型別的結構布局。一個 lambda 運算式對應于一個你不知道的結構,它的名字基本上是
class Unnameable {
~~~data members~~~
public:
auto operator()(~~~args~~~) const { ~~~body~~~ }
};
其中所有~~~位都是根據 lambda 的形式填充的。例如,lambda [=](int x) { return x y; }(其中y是int從外部范圍捕獲的)將對應于這樣的結構布局:
class Unnameable {
int y;
public:
auto operator()(int x) const { return x y; }
};
當y通過復制進行捕獲時,這種轉換非常明顯。(在您參考的部分上方幾行描述了按副本捕獲的情況。)但是,當y通過參考進行捕獲時,轉換并不明顯。編譯器可以將其降低為
class Unnameable {
int& y;
public:
auto operator()(int x) const { return x y; }
};
但是這樣class Unnameable就不能復制分配,實際上閉包物件確實需要復制分配[哎呀-不,閉包不可分配!所以實際上我不確定為什么這不能正常作業。但是無論如何,請參閱下面的巧妙技術]。因此,編譯器實際上將其降低為更像
class Unnameable {
int *py;
public:
auto operator()(int x) const { return x *py; }
};
這就是為什么措辭對于“在通過參考捕獲的物體的閉包型別中宣告的附加未命名非靜態資料成員”的確切含義如此含糊。它只保證“如果宣告,此類非靜態資料成員應為文字型別”(即,它們不會意外地使閉包物件對 constexpr 不友好)。
現在,標準中的措辭實際上允許“足夠智能的編譯器”走得更遠——盡管沒有供應商真正這樣做,AFAIK。假設有問題的 lambda 是這個:
int a = 1, b = 2;
auto lam = [&]() { return a b; };
std::function<int()> f = lam;
a = 3;
assert(f() == 5);
由于編譯器碰巧知道函式堆疊幀的布局a,b因此編譯器完全允許使用這種結構布局生成閉包型別:
class Unnameable {
int *p; // initialized with &a, which happens to be &b-1
public:
auto operator()() const { return p[0] p[1]; }
};
標準可以通過說諸如“對于通過參考捕獲的每個物體,在閉包型別中宣告一個未命名的非靜態資料成員。如果物體是 type T,那么這種資料成員的型別是 '指向remove_cvref_t<T>.'的指標。”但這會禁止這種巧妙的實作技術,所以,他們只是不指定它。
uj5u.com熱心網友回復:
P0170R1 (constexpr lambda)添加了對文字型別的要求。目的是確保按參考捕獲可在 constexpr 中創建和使用。
我不認為嚴格需要閉包型別的參考成員。實作可以選擇為每個按參考捕獲添加一個指標成員,或者如果它知道不使用這樣的捕獲,甚至不添加任何成員。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/516304.html
