Generic lambda with std::function does not capture variables

流过昼夜 提交于 2019-12-04 09:57:02

问题


I'm trying to use the generic lambda of C++14, but got a trouble with 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);
}

This fails to compile with an error message saying that error: ‘a’ was not declared in this scope.

It works if I change it to (int b).

Is it a bug? or am I missing something?

The version of GCC i'm using is 4.9.2.


回答1:


I can reproduce this unless I do any of the following:

  • remove const from a
  • name a in the capture-list
  • change std::function<void(int)> to auto
  • make the lambda non-generic by changing auto b to int b
  • use Clang (e.g. v3.5.0)

I believe that this is a compiler bug related to optimisations and a failure to detect odr-use in a generic lambda (though it's interesting that setting -O0 has no effect). It could be related to bug 61814 but I don't think it's quite the same thing, therefore:

I have raised it as GCC bug 64791.

  • (Update: this bug has since been marked as fixed in GCC 5.0.)

Certainly I can't find anything obvious in the C++14 wording that should disallow your code, though there's very little "obvious" in general in the new C++14 wording. :(


[C++14: 5.1.2/6]: [..] For a generic lambda with no lambda-capture, the closure type has a public non-virtual non-explicit const conversion function template to pointer to function. The conversion function template has the same invented template-parameter-list, and the pointer to function has the same parameter types, as the function call operator template. [..]

[C++14: 5.1.2/12]: A lambda-expression with an associated capture-default that does not explicitly capture this or a variable with automatic storage duration (this excludes any id-expression that has been found to refer to an init-capture's associated non-static data member), is said to implicitly capture the entity (i.e., this or a variable) if the compound-statement:

  • odr-uses (3.2) the entity, or
  • names the entity in a potentially-evaluated expression (3.2) where the enclosing full-expression depends on a generic lambda parameter declared within the reaching scope of the lambda-expression.

[ Example:

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 ] All such implicitly captured entities shall be declared within the reaching scope of the lambda expression. [ Note: The implicit capture of an entity by a nested lambda-expression can cause its implicit capture by the containing lambda-expression (see below). Implicit odr-uses of this can result in implicit capture. —end note ]

[C++14: 5.1.2/13]: An entity is captured if it is captured explicitly or implicitly. An entity captured by a lambda-expression is odr-used (3.2) in the scope containing the lambda-expression. [..]




回答2:


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

Don't know if it should work with std::function but this works for sure.

Further investigation:

I created a class to mimic as closely as possible the 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

This suggests that your example should have worked. After all lambdas are the same in behavior with an object who has the operator() overloaded and fields for the captured variables.

If we change the class to get to the auto part:

This doesn't work:

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

However this works:

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

So most likely it is related to the use of auto as parameter of lambda/functions, a feature new to C++14 so most likely without a mature implementation.



来源:https://stackoverflow.com/questions/28141403/generic-lambda-with-stdfunction-does-not-capture-variables

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!