'Return a lambda from a lambda

I want to use a lambda to evaluate (switch-case) some conditions and return a lambda accordingly.

const auto lmb1 = []() {
    printf("1\n");
};

const auto lmb2 = []() {
    printf("2\n");
};

const auto select = [](auto const &ref) {
  switch(ref) {
      case 1: return lmb1;
      case 2: return lmb2;
  }
  
    
};

std::function foo = select(1);

foo();

Sadly things aren't working.
What I am doint wrong?



Solution 1:[1]

The problem is that a lambda, by default, deduce (as an auto function) the returned type and in your lambda you return two different lambdas. Every lambda has a different type, so the compiler can't choose a type for the returned lambda

[](auto const &ref) {
  switch(ref) {
      case 1: return lmb1; //    decltype(lmb1) 
      case 2: return lmb2; // != decltype(lmb2)
  }  
};

You can solve the problem in different ways.

  1. You can explicit the lambda returned type, using a type that both lmb1 and lmb2 can be converted to (in this example, std::function<void()> or also void(*)()). When that type is available

    // -----------------vvvvvvvvvvvvvvvvvvvvvvvv
    [](auto const &ref) -> std::function<void()> {
         switch(ref) {
             case 1: return lmb1; 
             case 2: return lmb2;
         }  
    };
    
  2. You can explicitly convert the returned values to a common type. Again: when a common type is available

    [](auto const &ref)
    {
        switch(ref) { // --V
            case 1: return +lmb1; // with the '+', lmb1 is converted to a void(*)()
            case 2: return +lmb2; // with the '+', lmb2 is converted to a void(*)()
    }     // --------------^
    
  3. If the ref argument can be a template value, starting from you can define a template lambda and, using if constexpr, you can return different types from different lambdas. This works also when there isn't a common type (but require a compile-time known argument)

    const auto selct = []<int REF>(std::integral_constant<int, REF>) {
      if constexpr ( 1 == REF )
        return lmb1;
      else
        return lmb2;
    };
    
    auto foo = selct(std::integral_constant<int, 1>{});
    

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1