Search code examples
c++templateslambdac++20variadic-templates

Type of lambda with parameter pack


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.


Solution

  • 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)>`