在c ++中的Lambda表达式,OS X的clang vs GCC

时间:2014-07-19 10:07:27

标签: macos gcc c++11 clang lambda

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的行为是正确的!

2 个答案:

答案 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表达式的上下文中完成的(而不是生成的类),因此参数名称确实隐藏了变量名称。封闭的功能。