I have a variable that I share in a header file. It is initialized with a lambda. I wish to call a function from another translation unit, taking this lambda variable as a parameter.
With gcc, I get an error:
used but never defined [-fpermissive].
Here is a cut down example:
// in shared header file
//
const auto make_lambda() { return [](){ }; }
inline const auto lambda = make_lambda();
// in cpp file
//
void func(const decltype(lambda)&);
int main() {
func(lambda);
}
My understanding is that the lambda type should be shared between translation units.
When I alter the lambda variable to the following, the issue goes away:
inline const auto lambda = [](){ }; // <-- this does work
Clang provides an excellent diagnostic that explains the problem with make_lambda
well:
<source>:8:6: error: function 'func' is used but not defined in this translation unit,
and cannot be defined in any other translation unit because its type
does not have linkage
void func(const decltype(lambda)&);
^
Having "no linkage" means:
When a name has no linkage, the entity it denotes cannot be referred to by names from other scopes.
The reason why our lambda has no linkage is that the closure type it defines is defined inside of make_lambda
. This results in:
Names not covered by these rules have no linkage. Moreover, except as noted, a name declared at block scope has no linkage.
The type you're trying to use is make_lambda::__lambda
essentially, and this cannot be used across different TUs.
inline const auto lambda = [](){ };
... makes the compiler errors go away, but this makes your program ill-formed, no diagnostic required. We must respect the one-definition-rule, and we violate the following:
In each such definition, except within the default arguments and default template arguments of
D
, corresponding lambda-expressions shall have the same closure type (see below).
Every TU will have its own unique closure type, but lambda
must have the same definition everywhere. This is an ODR violation.
If you really insisted on doing it, the safe way would be to put it into a class type:
struct dummy {
using lambda_type = [] {};
};
dummy::lambda_type
will have linkage and doesn't violate the ODR.
See also: [basic.def.odr]/16
Just don't share lambdas across different TUs. Even if you make it work, the solution isn't pretty. Instead, consider:
std::function
All of these options are much better than trying to share a lambda expression (outside of functions) across TUs.