4

In the following minimal example:

int main()
{
    const int foo = 1;
    const auto a = foo == 1 ? [](){return 42;} : [](){return 4;};
    const auto b = foo == 1 ? [foo](){return 42;} : [foo](){return 4;};
}

a is fine. b however is not, because:

<source>:5:29: error: incompatible operand types ('(lambda at <source>:5:31)' and '(lambda at <source>:5:53)')

    const auto b = foo == 1 ? [foo](){return 42;} : [foo](){return 4;};
                            ^ ~~~~~~~~~~~~~~~~~~~   ~~~~~~~~~~~~~~~~~~

Why is that the case? And how can the intended b be obtained?

Tobias Hermann
  • 9,936
  • 6
  • 61
  • 134
  • 1
    The const-ness of `foo` might be relevant for the underlying problem, but not for the question as stated. – MSalters Nov 12 '19 at 13:03

2 Answers2

5

Capturing doesn't make the lambda unique; the lambda type is already unique by definition. However, non-capturing lambda's can be converted to function pointers, and that creates a common type in your first example.

You solve your specific example as follows:

const auto b = [foo](){ return (foo == 1) ? 42 : 4;};
MSalters
  • 173,980
  • 10
  • 155
  • 350
  • A decent optimizer will turn this into `const auto b = []() { return 42; }` when it eliminates the constant `foo`.. – MSalters Nov 12 '19 at 13:14
  • I don't think it can remove the capture though :/ (because of ABI I think). – Jarod42 Nov 12 '19 at 13:26
  • @Jarod42: At the binary level, there are three parts to the capture: the storage, initializing the captured value, and using it. The first cannot be eliminated anyway because even non-capturing lambda's have `sizeof > 0`. Initialization and use can be eliminated. – MSalters Nov 12 '19 at 13:36
  • Whereas we might expect `sizeof([](){return 42;}) == 1`, we have `sizeof([foo](){ /*..*/}) >= sizeof (foo)` even with optimized out `foo`. (whereas we might naively expect a size of 1) :-( – Jarod42 Nov 12 '19 at 13:44
  • @Jarod42: That might be the quantum rule of optimizers: looking at the result can change it. – MSalters Nov 14 '19 at 16:24
3

Type of lambda is unique even if it does not capture anything. The way you compare them is not correct, you should compare them directly:

auto first{[](){return 42;}};
auto second{[](){return 42;}};
static_assert(not ::std::is_same_v<decltype(first), decltype(second)>);

avoiding possible implicit conversion to pointer to function that happens when automatic deduction of type of variable is performed:

auto a = foo == 1 ? [](){return 42;} : [](){return 4;};
static_assert(::std::is_same_v<int ( * ) (void) , decltype(a)>);
user7860670
  • 35,849
  • 4
  • 58
  • 84