Home > front end >  Type of lambda with parameter pack
Type of lambda with parameter pack

Time:12-08

Consider the following (https://godbolt.org/z/sfT3aesvK):

#include <utility>
#include <vector>

struct A { constexpr static int type = 0; };

template <typename Func, typename... Args>
int foo(Func func, Args&& ... args) {
    auto call_with_A = [func](Args&& ... args) {
        return func.template operator()<A>(std::forward<Args>(args)...);
    };
    std::vector<int(*)(Args&&...) /* what goes here? */> vec{{call_with_A}};
    int acc = 0;
    for (auto fn : vec) {
        acc  = fn(std::forward<Args>(args)...);
    }
    return acc;
}

int bar() {
    return 1   foo([]<typename T>(int a, int b) {
        return T::type   a   b;
    }, 2, 3);
}

The above does not compile, because

no known conversion from '(lambda at <source>:8:24)' to 'int (*)(int &&, int &&)' for 1st argument

My question is what the template type T so that std::vector<T> will accept call_with_A as an element?

I tried to print what decltype(call_with_A) is, but this seems to just be a (lambda at [...]) expression for the compiler.

CodePudding user response:

The type of a lambda expression is "unutterable". It cannot be written down directly. However, you can declare a typedef alias for the type:

auto call_with_A = /* lambda */;
using LambdaType = decltype(call_with_A);
std::vector<LambdaType> vec = {call_with_A};

You can also use class template argument deduction if you don't need to mention the type anyway:

auto call_with_A = /* lambda */;
std::vector vec = {call_with_A};
// the type of `vec` is `std::vector<decltype(call_with_A)>`

CodePudding user response:

Every lambda has a different type - even they have the same signature. Lambda functions that have identical body also have different type.

You can declare a vector of type using decltype. However, it is not useful. For example.

template <typename Func, typename... Args>
int foo(Func func, Args&& ... args) {
    auto call_with_A = [func](Args&& ... args) {
        return func.template operator()<A1>(std::forward<Args>(args)...);
    };
    auto call_with_A1 = [func](Args&& ... args) {
        return func.template operator()<A1>(std::forward<Args>(args)...);
    };
    auto call_with_A2 = [func](Args&& ... args) {
        return func.template operator()<A1>(std::forward<Args>(args)...);
    };

    std::vector<decltype(call_with_A)> vec;
    vec.push_back(call_with_A);
    vec.push_back(call_with_A1); // Not OK
    vec.push_back(call_with_A2); // Not OK
    
    return 0;
}

Your best option is to use std::vector of std::function. For example.

template <typename Func, typename... Args>
int foo(Func func, Args&& ... args) {
    auto call_with_A = [func](Args&& ... args) {
        return func.template operator()<A1>(std::forward<Args>(args)...);
    };
    auto call_with_A1 = [func](Args&& ... args) {
        return func.template operator()<A1>(std::forward<Args>(args)...);
    };
    auto call_with_A2 = [func](Args&& ... args) {
        return func.template operator()<A1>(std::forward<Args>(args)...);
    };


    std::vector<std::function<int(Args...)>> vec;
    vec.push_back(call_with_A);
    vec.push_back(call_with_A1);
    vec.push_back(call_with_A2);
    
    return 0;
}
  • Related