【问题标题】:Are my lambda parameters really shadowing my locals?我的 lambda 参数真的会影响我的本地人吗?
【发布时间】:2016-05-24 03:39:59
【问题描述】:

我正在处理一些 C 代码,它获取一些数据,并将其转发给传入的函数:

void foo(int* data, void (*fun)(int*)){
  (*fun)(data);
};

以下工作没有警告:

void bar(int* data){};

int main(){
  int data=0;
  foo(&data,bar);
}

但是,如果我改用 lambda:

int main(){

  int data=0;
  foo(&data,[](auto data){});
}

我收到以下警告:

warning: declaration of ‘data’ shadows a previous local [-Wshadow]
   foo(&data,[](auto data){});
                         ^
o.cpp:14:7: note: shadowed declaration is here
   int data=0;

但我认为空捕获组会在查找过程中排除第一个实例化。

这个警告合法吗?
为什么空捕获不足以避免警告?

【问题讨论】:

  • 我认为该警告只是为了警告您,您可能认为您使用的是data 而不是data。它不知道你的 intent 是什么。
  • name 确实被遮住了。您无需捕获符号即可使用其名称,例如在未经评估的上下文中,例如decltype
  • 至少在 Visual Studio 2015 中,您不能在未评估的上下文中使用符号(即使 decltype(data) 或 sizeof(data) 因error C2065: 'data' undeclared identifier 而失败)
  • @AndreasH。请参阅 [expr.prim]/7 ,“[...] 出于名称查找的目的,[...] compound-statementlambda-expression 的上下文中被考虑”。看起来您的编译器在名称查找时失败了,这与是否发生捕获是分开的。

标签: c++ gcc lambda clang compiler-warnings


【解决方案1】:

来自 lambda 封闭范围的名称也在 lambda 范围内。

未捕获的名称仍然可以使用,只要它们不是odr-used。只有 odr-used 变量必须被捕获。示例:

#include <iostream>

template<typename T> void foo(const int *, T f) { std::cout << f(5) << '\n'; }

int main()
{
    const int data=0;
    foo(&data,[](int baz){
        return data;
    });
}

因为读取常量表达式不是odr-use,所以这段代码是正确的,data指的是main中的变量。

这个程序输出0,但是如果你把int baz改成int data,它就会输出5

【讨论】:

  • 一个空的捕获列表不会捕获当前范围内的任何变量(参见此处en.cppreference.com/w/cpp/language/lambda)。 Visual Studio 2015 不会编译您的示例并失败并出现错误 C3493(如果未给出默认捕获类型,则不会隐式捕获“数据”)
  • @AndreasH 我的回答的重点是这些名称在范围内,即使没有被捕获,也可能以有限的方式使用
  • 这意味着我可以写 [](int baz) { return sizeof(data);因为这不是对数据的 odr 使用(所以不需要捕获),它应该仍然在 lambda 的范围内 - 或者我错过了什么?
  • 是的,你可以这样写
  • 在您链接的页面上,搜索const int x - 有一个与我的非常相似的示例。也许 MSVC 被窃听了
【解决方案2】:

参考 MISRA C++ 2008: 在内部作用域中声明的标识符不应与在外部作用域中声明的标识符具有相同的名称。

在您的示例中,int data 在外部范围中声明,但通过引用进入 lambda 的内部范围。问题是您的 lambda 参数列表中还有一个参数,称为数据(内部范围)。这导致数据变量从 lambda 的外部范围隐藏。

顺便说一句。您的第一个函数指针示例也应该重写,因为内部和外部范围内的标识符命名也存在冲突。在这种情况下,它并不是真的很糟糕,因为内部分数中只有一个数据变量在使用中。但是,当参数列表变量和调用函数的外部范围的变量具有相同的名称时,这可能会导致程序员混淆,也应该避免。

【讨论】:

    猜你喜欢
    • 2017-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-25
    • 1970-01-01
    • 2017-12-05
    相关资源
    最近更新 更多