Lambda表达式捕获匿名union成员的编译器行为合规性探讨
匿名联合体是未命名的联合体定义,不会同时声明变量,其成员会直接注入外围作用域。但在Lambda表达式中捕获这些成员时,不同编译器的行为差异很大,我们结合C++标准来明确哪种行为是合规的。
示例代码
int main() { union { int x{0}; char y[4]; }; // 所有编译器均报错 //[=]() { return x; }; //[&]() { return x; }; // 仅GCC支持 [x]() { return x; }; [&x]() { return x; }; // 所有编译器均支持 [x=x]() { return x; }; [&x=x]() { return x; }; }
不同捕获方式的编译器表现
1. 隐式捕获([=]/[&])
所有编译器均拒绝这种写法,例如Clang的错误提示:
unnamed variable cannot be implicitly captured in a lambda expression
2. 显式捕获([x]/[&x])
编译器行为存在明显分歧:
- MSVC报错:
error C3492: 'x': you cannot capture a member of an anonymous union
- Clang报错:
'x' in capture list does not name a variable
- GCC允许该写法且无警告
3. 初始化捕获([x=x]/[&x=x])
所有编译器均支持这种写法。
标准合规性判断
根据C++标准的相关规定:
隐式捕获:隐式捕获的目标是外围作用域的独立变量,而匿名联合体的成员本质上是无名联合体对象的成员,并非独立变量。因此编译器拒绝隐式捕获是符合标准的,这部分所有编译器行为正确。
显式捕获:Lambda的显式捕获列表要求捕获的是变量,而匿名联合体的成员并不是独立变量,而是无名联合体对象的成员(该联合体对象没有标识符)。因此直接写
[x]或[&x]捕获是不符合标准的——MSVC和Clang的报错行为符合标准,GCC的支持属于编译器扩展,并非标准要求的行为。初始化捕获:初始化捕获的语法是通过表达式初始化Lambda的捕获变量,这里的
x=x是将外围作用域中匿名联合体成员x的值(或引用)绑定到Lambda的捕获变量上,标准允许这种通过表达式初始化捕获的形式,因此所有编译器的支持是符合标准的。
内容的提问来源于stack exchange,提问作者Fedor

