Consider the following code:
main()
{
bool t;
...
std::function<bool (bool)> f = t ? [](bool b) { return b; } : [](bool b) { return !b; }; // OK
std::function<bool (bool)> f = t ? [t](bool b) { return t == b; } : [t](bool b) { return t != b; }; // error
}
When compiled with Clang 3.1, the assignment of non-capture lambda works while the one with captures fails:
main.cpp:12:36: error: incompatible operand types ('<lambda at main.cpp:12:38>' and '<lambda at main.cpp:12:71>')
std::function<bool (bool)> f2 = t ? [t](bool b) { return t == b; } : [t](bool b) { return t != b; }; // error
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Why does capturing the same variable causes the 2 lambdas to be of incompatible types?
The type of a lambda is "a unique, non-union class type" called the closure type. Each lambda is implemented as a different type, local to the scope of declaration, which has an overloaded operator () to call the function body.
Example: if you write this:
auto a=[t](bool b){return t==b;};
auto b=[t](bool b){return t!=b;};
Then the compiler compiles this (more or less):
class unique_lambda_name_1
{
bool t;
public:
unique_lambda_name_1(bool t_) t(_t) {}
bool operator () (bool b) const { return t==b; }
} a(t);
class unique_lambda_name_2
{
bool t;
public:
unique_lambda_name_2(bool t_) t(_t) {}
bool operator () (bool b) const { return t!=b; }
} b(t);
a and b have different types and can't be used in the ?: operator.
However, §5.1.2(6) says, that the closure type of a lambda with no capture has a non-explicit, public conversion operator, which converts the lambda to a function pointer - non-closures can be implemented as simple functions. Any lambda with the same argument and return types can be converted to the same type of pointer and so the ternary ?: operator can be applied to them.
Example: the non-capture lambda:
auto c=[](bool b){return b;};
is implemented like this:
class unique_lambda_name_3
{
static bool body(bool b) { return b; }
public:
bool operator () (bool b) const { return body(b); }
operator decltype(&body) () const { return &body; }
} c;
which means that this line:
auto x = t?[](bool b){return b;}:[](bool b){return !b;};
means actually this:
// a typedef to make this more readable
typedef bool (*pfun_t)(bool);
pfun_t x = t?((pfun_t)[](bool b){return b;}):(pfun_t)([](bool b){return !b;});
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