Since neither lambda capture they can be converted to function pointers with compatible signatures, so gcc
and clang
are correct here.
There is a gcc bug report which summarizes this topic well: [c++ lambda] error in assigning lambda expr though "operator?:" while capturing that covers this and says:
The compiler behaviour looks correct to me. The difference of the
lambda expressions in bar and foo3
compared to the other two is that
these are capture-free lambdas and thus have a conversion function to
function pointer.
Each lambda expression corresponds to a unique class type, so what we
have in foo1
and foo2
can be compared with the following
class-example:
struct A{}; struct B{};
void f() { false ? A() : B(); }
This expression has no common type for the conditional operator and is
ill-formed.
What we have in bar
and foo3
can be compared with the following
class-example :
struct A
{
typedef void (*F)();
operator F();
};
struct B
{
typedef void (*F)();
operator F();
};
void f() { false ? A() : B(); }
This is well-formed, because in the last step of the conditional
operator conversion attempts (5.16p5), more general conversions are
attempted and these find the common pointer to function.
5.16p5
says:
Otherwise, the result is a prvalue. If the second and third operands
do not have the same type, and either has (possibly cv-qualified)
class type, overload resolution is used to determine the conversions
(if any) to be applied to the operands (13.3.1.2, 13.6). If the
overload resolution fails, the program is ill-formed. Otherwise, the
conversions thus determined are applied, and the converted operands
are used in place of the original operands for the remainder of this
section.
If we change your code as follows:
int x = 20 ;
auto increasing = [&x](int lhs, int rhs){return lhs < rhs;};
auto decreasing = [&x](int lhs, int rhs){return lhs > rhs;};
both gcc
and clang
generate an error, clang
says (see it live):
error: incompatible operand types ('(lambda at prog.cc:8:23)' and '(lambda at prog.cc:9:23)')
std::sort(v.begin(), v.end(), increase ? increasing : decreasing);
^ ~~~~~~~~~~ ~~~~~~~~~~
For reference the draft C++11 standard 5.1.2
[expr.prim.lambda] says:
The closure type for a lambda-expression with no lambda-capture has a
public non-virtual non-explicit const conversion function to pointer
to function having the same parameter and return types as the closure
type’s function call operator. The value returned by this conversion
function shall be the address of a function that, when invoked, has
the same effect as invoking the closure type’s function call operator.
The wording is modified in the draft C++14 standard but does not alter this property.
Update
Filed a bug report.