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.
Capture clause A lambda can introduce new variables in its body (in C++14), and it can also access, or capture, variables from the surrounding scope. A lambda begins with the capture clause. It specifies which variables are captured, and whether the capture is by value or by reference.
Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.
Yes, sure. Normal name lookup rules apply.
Permalink. All the alternatives to passing a lambda by value actually capture a lambda's address, be it by const l-value reference, by non-const l-value reference, by universal reference, or by pointer.
I can reproduce this unless I do any of the following:
const
from a
a
in the capture-list
std::function<void(int)>
to auto
auto b
to int b
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.
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. [..]
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With