c++lambdareturn-type-deduction

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

  • 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>{});