非捕获变量能被lambda参数遮蔽吗?

6

我有一段代码看起来像这样 - 这只是一个大大简化的片段,但这个片段编译并表现出了相同的行为:

template <typename TFunc>
float FloatSelect( const float in_value, TFunc&& Predicate) {
  return std::forward<TFunc>(Predicate)(in_value) ? in_value : 0.0f;
};

void DisplayFloatSelect() {
  const float value = FloatSelect(
    -1.0f,
    [] (const float value) { return value > 0.0f; }
  );

  std::cout << value << std::endl;
}

启用 -Wshadow 后,编译器会发出以下警告(如此处所示):

12 : warning: declaration shadows a local variable [-Wshadow]

[] (const float value) { return value > 0.0f; }

^

10 : note: previous declaration is here

const float value = FloatSelect(

^

这并没有什么帮助 - 我明白变量阴影的含义,但是由于lambda没有捕获任何东西,在这里应该没问题。

我错过了什么吗?


6
这是有益的,因为也许你本来想要捕捉它,但由于变量名冲突而没有成功(并且你没有收到相关的错误信息)。 - Borgleader
什么都没有,这个警告毫无意义。 - user2249683
您在两个嵌套的上下文中使用了相同的变量名,编译器不知道这是否是有意的,因此会发出警告。这就是该警告的目的。原始的“value”在lambda体中是可见的,但在没有捕获它的情况下引用它是不合法的。 - Jonathan Wakely
评论是用来请求澄清的,伙计们,而不是提供答案。请加入我↓↓↓↓↓↓↓↓↓↓↓ - Lightness Races in Orbit
您帖子标题中的问题在帖子中并未提及。而帖子中提到的问题则不够强调。 - R Sahu
看着这段代码,我闻到了鱼腥味。为什么会有两个变量在重叠的作用域中使用相同的名称?这段代码不是因为匆忙的重新组织而产生的吗?它的作者是否意识到它的作用?或者这可能是传达某种想法失败的结果?如果编译器发出警告,它应该得到赞扬。 - ach
2个回答

13
是的,非捕获变量可以被lambda参数遮盖。
在OP中的lambda特定情况下,您可能会认为内部声明的value不会遮盖外部声明的范围,因为lambda没有捕获。尽管如此,在lambda主体内仍然可以看到外部的value,因为lambda的主体仍然在封闭块的范围内:
(C++14 §5.1.2/p.7):lambda表达式的复合语句产生函数调用运算符的函数体(8.4),但是对于名称查找的目的(3.4),确定此(9.3.2)的类型和值并将idexpressions转换为非静态类成员的类成员访问表达式使用(* this)(9.3.1),复合语句被认为处于lambda表达式的上下文中。

非捕获变量的odr-use是一个错误,但如果lambda表达式没有捕获,它可以使用外部作用域中定义的名称,如果它不是odr-use(在这种情况下,变量不会被捕获)。特别地,可以使用外部作用域中的const变量:

const int i = 20;
int f = ([](){return i + 3;})();

即使lambda没有捕获,一个名为i的显式参数肯定会遮盖外部的i。(请参见http://coliru.stacked-crooked.com/a/006f5f20cca841d5;您可能需要尝试启用-Wshadow。)
由于-Wshadow恰好旨在揭示这种模糊的名称使用方式,所以在OP的情况下触发警告似乎并不太令人惊讶。
-Wshadow既不是通过-Wall也不是通过-Wextra启用的,这正是因为它经常会警告您关心的事情。

谢谢,这正是我在寻找的答案 - 这段代码确实不好,但我很好奇警告的原因。const变量的例子说明了一切。 - Gama

2
您错过了编译器不完美的事实,有时针对相对较新的语言特性发出荒谬的警告,在这种情况下,这些警告并没有太多意义。另一方面,警告通常是为了确保您真正想要编写的代码,而不是其他代码。也许您想捕获外部的value,但由于某种原因(也许您没有注意),未能捕获它并重新声明它。在这段代码中,这似乎有点牵强,但重点是警告并不总是告诉您关于您编写的代码:有时它们会告诉您编译器认为您可能想要编写的代码。然后很容易使用不同的名称来表示“是的,我知道”。这也会产生更清晰的代码和明确的意图。

在尝试编写无警告代码时,有时候编译器会告诉你可能想要编写的代码,这个让人很烦恼。 - user2249683
@DieterLücking:这种情况极其罕见,而且通常发生在您的代码不够清晰的情况下。我在我的答案中解决了这个问题。 - Lightness Races in Orbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接