c ++的lambda表达式的一个特定属性是捕获声明它们的作用域中的变量。例如,我可以在lambda函数中使用声明和初始化的变量c,即使' c'不是作为参数发送的,而是由' []':
捕获的 #include<iostream>
int main ()
{int c=5; [c](int d){std::cout<<c+d<<'\n';}(5);}
因此预期输出为10.当至少有两个变量(一个被捕获而另一个作为参数发送)具有相同的名称时,就会出现问题:
#include<iostream>
int main ()
{int c=5; [c](int c){std::cout<<c<<'\n';}(3);}
我认为2011版c ++标准表示,如果名称重合,捕获的变量优先于lambda表达式的参数。事实上,在Linux上使用GCC 4.8.1编译代码,我得到的输出是预期的5
。如果我使用apple的clang编译器(clang-503.0.40,Mac OS X 10.9.4上的Xcode 5.1.1附带的那个)编译相同的代码,我得到另一个答案,{{1} }。
我试图弄清楚为什么会这样;它只是一个苹果的编译器错误(如果该语言的标准真的说被捕获的&#39; c&#39;具有优先权)或类似的东西?这个问题能解决吗?
修改
我的老师给GCC服务台发了一封电子邮件,他们回答说这显然是GCC编译器的错误并向Bugzilla报告。所以Clang的行为是正确的!
答案 0 :(得分:2)
从我对c ++ 11标准的理解来看:
5.1.2 Lambda表达式
3 lambda-expression的类型(也是闭包对象的类型)是一个唯一的,未命名的非联合类类型 - 被称为 闭包类型 - 其属性如下所述。
...
5 lambda-expression的闭包类型有一个公共内联函数调用操作符(13.5.4),其参数和返回类型为 由lambda-expression的parameter-declaration-clause和 分别为trailing-return-type。这个函数调用运算符是 声明const(9.3.1)当且仅当lambda表达式时 参数声明子句后面没有可变的。
...
14 对于通过复制捕获的每个实体,在闭包类型中声明一个未命名的非静态数据成员
像这样的lambda表达式......
int c = 5;
[c](int c){ std::cout << c << '\n'; }
...大致相当于这样的类/结构:
struct lambda
{
int c; // captured c
void operator()(int c) const
{
std::cout << c << '\n';
}
};
所以我希望参数能够隐藏被捕获的成员。
<强> 编辑: 强>
从标准(上面引用的)开始 14 ,似乎从捕获的变量创建的数据成员是 * 未命名 * 。引用它的机制似乎与普通标识符查找无关:
17 每个id-expression都是由副本捕获的实体的odr-use(3.2)转换为对相应的未命名数据成员的访问权限关闭类型。
从我对标准的阅读中不清楚这种转换是否应该优先于参数符号查找。
所以也许这应该标记为 UB (未定义的行为)?
答案 1 :(得分:1)
来自C ++ 11 Standard,5.1.2&#34; Lambda表达式&#34; [expr.prim.lambda]#7:
lambda-expression 的复合语句产生函数调用运算符的函数体(8.4), 但是为了查找名称(3.4),确定this
(9.3.2)的类型和值并转换 id-expression s 使用(*this)
(9.3.1)将非静态类成员引用到类成员访问表达式中, 复合语句在 lambda-expression 的上下文中被考虑。
另外,从3.3.3&#34;块范围&#34; [basic.scope.local]#2:
函数参数名称的潜在范围(包括一个出现在 lambda-declarator 中)或 函数定义(8.4)中的函数本地预定义变量从其声明点开始。
捕获列表中的名称不是声明,因此不会影响名称查找。捕获列表只允许您使用局部变量;它没有将自己的名字引入lambda的范围。例如:
int i, j;
int main()
{
int i = 0;
[](){ i; }; // Error: Odr-uses non-static local variable without capturing it
[](){ j; }; // OK
}
因此,由于lambda的参数位于内部块范围内,并且由于名称查找是在lambda表达式的上下文中完成的(而不是生成的类),因此参数名称确实隐藏了变量名称。封闭的功能。