Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does returning lambdas between branches of ternary operator work for some lambdas?

Tags:

c++

lambda

I want to choose a lambda depending on some conditional, but the compiler, for some lambdas, says that the types of the lambdas do not match between the branches of the ternary operator.

The following code compiles:

int flag = 4;
auto result = flag % 2
    ? [](int x) {
        return x + x;
    }
    : [](int x) {
        return x * x;
    };

but the following 2 snippets do not compile:

int flag = 4;
auto result = flag % 2
    ? [flag](int x) {
        return x + flag;
    }
    : [flag](int x) {
        return x - flag;
    };

auto result2 = flag % 2
    ? [](auto x) {
        return x + x;
    }
    : [](auto x) {
        return x * x;
    };

resulting in the following errors respectively:

test.cpp: In function 'auto f(int)':
test.cpp:8:9: error: operands to ?: have different types 'f(int)::<lambda(int)>' and 'f(int)::<lambda(int)>'
         ? [flag](int x) {    

test.cpp: In function 'auto f(int)':
test.cpp:10:9: error: operands to ?: have different types 'f(int)::<lambda(auto:1)>' and 'f(int)::<lambda(auto:2)>'
         ? [](auto x) {
         ^

Why do the last 2 snippets not compile?

like image 629
Michael Tsang Avatar asked Apr 01 '17 11:04

Michael Tsang


2 Answers

The first snippet compiles because both lambdas are implicitly convertible to int(*)(int), so the compiler can use that as the type of the ?: expression and thus to deduce the type of result.

If the capture list is non-empty (case 2) no such conversion to pointer-to-function exists (5.1.2/6 in N4141), so we end up with two unrelated types with no common implicit conversion target as the 2nd and 3rd operand of operator?: and thus the type of the ternary expression can no longer be deduced.

In case 3 we have a generic lambda, which, if the capture list is empty, has a conversion operator with a so called invented template-parameter-list which defines a set possible conversions. In our specific case here, the generic lambdas are convertible to T2(*)(T1), where T1 is the type deduced for the argument and T2 is lambda's deduced return type. Long story short: There is no rule to pick a "best conversion" from that set, so again, the compiler cannot deduce a type for our ternary expression.

like image 87
Baum mit Augen Avatar answered Oct 09 '22 20:10

Baum mit Augen


Ternary Expression type is a common type for both true and false branch. Operator tries to find this type by trying to convert both branches, using complicated rules. But two different lambdas have different, distinct, non-compatible types. Generally, they are not convertible to each other, so you got an erron for your second and third try.

However, certain kind of lambdas (non-campuring, non-templated) can be converted to function pointer. If you have two lambdas, convertible to same function ponter type, ternary operator deduces that pointer to be the common type.

like image 37
Revolver_Ocelot Avatar answered Oct 09 '22 21:10

Revolver_Ocelot