带有std :: function的泛型lambda不捕获变量

时间:2015-01-25 20:56:12

标签: c++ gcc c++14

我试图使用C ++ 14的通用lambda,但是在使用std :: function时遇到了麻烦。

#include <iostream>
#include <functional>

int main()
{
    const int a = 2;
    std::function<void(int)> f = [&](auto b) { std::cout << a << ", " << b << std::endl; };
    f(3);
}

无法编译,并显示错误消息error: ‘a’ was not declared in this scope

如果我将其更改为(int b),则无效。

这是一个错误吗?或者我错过了什么?

我使用的GCC版本是4.9.2。

2 个答案:

答案 0 :(得分:23)

除非我执行以下任何操作,否则我可以重现此内容:

  • const
  • 中移除a
  • capture-list
  • 中的名称a
  • std::function<void(int)>更改为auto
  • 通过将auto b更改为int b
  • ,使lambda成为非泛型
  • 使用Clang(例如v3.5.0)

我认为这是一个与优化相关的编译器错误,并且无法在通用lambda中检测 odr-use (尽管设置-O0没有意义,这很有意思影响)。它可能与bug 61814有关,但我认为它相当同样的事情,因此:

我已将其提升为GCC bug 64791

  • (更新:此错误已在GCC 5.0中标记为已修复。)

当然,我无法在C ++ 14中找到任何明显不应该禁用你的代码的内容,尽管很少有明显的&#34;一般来说在新的C ++ 14中的措辞。 :(


  

[C++14: 5.1.2/6]: [..] 对于没有lambda-capture的通用lambda,闭包类型有一个公共的非虚拟非显式const转换函数模板来指向函数。转换函数模板具有相同的发明 template-parameter-list ,并且指向函数的指针具有与函数调用操作符模板相同的参数类型。 [..]

     

[C++14: 5.1.2/12]: lambda-expression ,其关联的 capture-default 未明确捕获此变量或具有自动存储持续时间的变量(这不包括任何<已发现引用 init-capture 的相关非静态数据成员的em> id-expression 被称为隐式捕获实体(即,如果复合语句

,则为this或变量      
      
  • odr-uses(3.2)实体,或
  •   
  • 在潜在评估表达式(3.2)中命名实体,其中封闭的full-expression依赖于在 lambda-expression 的到达范围内声明的泛型lambda参数。
  •   
     

[例如:

void f(int, const int (&)[2] = {}) { } // #1
void f(const int&, const int (&)[1]) { } // #2
void test() {
  const int x = 17;
  auto g = [](auto a) {
    f(x); // OK: calls #1, does not capture x
  };

  auto g2 = [=](auto a) {
    int selector[sizeof(a) == 1 ? 1 : 2]{};
    f(x, selector); // OK: is a dependent expression, so captures x
  };
}
     

-end example] 所有这些隐式捕获的实体都应在lambda表达式的到达范围内声明。 [注意:嵌套的lambda表达式对实体的隐式捕获可能导致其通过包含的lambda表达式进行隐式捕获(见下文)。隐式 odr-uses 会导致隐式捕获。 -end note]

     

[C++14: 5.1.2/13]:如果明确或隐式捕获实体,则捕获该实体。由 lambda-expression 捕获的实体在包含 lambda-expression 的范围内使用odr(3.2)。 [..]

答案 1 :(得分:3)

int main() {
    const int a = 2;
    auto f = [&](auto b) { std::cout << a << ", " << b << std::endl; };
    f(3);
}

不知道它是否适用于std::function,但这肯定有用。

进一步调查:

我创建了一个类来尽可能地模仿lambda:

class Functor {
private:
  int const x;

public:
  Functor() : x{24} {}
  auto operator()(int b) const -> void { cout << x << " " << b << endl; }
};


std::function<auto(int)->void> f2 = Functor{};
f2(3); // <- this works

这表明你的例子应该有效。所有lambda与行为相同,其中operator()重载的对象和捕获变量的字段。

如果我们更改课程以转到auto部分:

这不起作用:

class Functor {
private:
  int const x;

public:
  Functor() : x{24} {}
  auto operator()(auto b) const -> void { cout << x << " " << b << endl; }
};

std::function<auto(int)->void> f2 = Functor{}; // <-- doesn't work

然而这有效:

class Functor {
private:
  int const x;

public:
  Functor() : x{24} {}
  template <class T>
  auto operator()(T b) const -> void { cout << x << " " << b << endl; }
};

std::function<auto(int)->void> f2 = Functor{}; // <-- this works

所以很可能它与使用auto作为lambda / functions的参数有关,这是C ++ 14的新功能,所以很可能没有成熟的实现。