Search code examples
c++c++17variadic-templatestemplate-meta-programminggeneric-lambda

Get argument type of lambda with auto specifier


I have a meta function which gives me the type of the I-th argument of a lambda/function:

#include <iostream>
#include <tuple>

namespace details
{
    //! Spezialization for Funktion-Pointers
    template<typename Ret, typename... Args>
    std::tuple<Args...> getArgs(Ret (*)(Args...));

    //! Spezialization for Functor/Lambdas
    template<typename F, typename Ret, typename... Args>
    std::tuple<Args...> getArgs(Ret (F::*)(Args...));

    //! Spezialization for Functor/Lambdas
    template<typename F, typename Ret, typename... Args>
    std::tuple<Args...> getArgs(Ret (F::*)(Args...) const);

}; // namespace details

template<typename F, std::size_t I>
using GetArg = std::tuple_element_t<I, decltype(details::getArgs(std::declval<F>()))>;

int main()
{
    auto f1 = [](int a, int b){};
    static_assert(std::is_same<GetArg<decltype(f1), 0>, int>{}, "Not the same!");

    // auto f2 = [](int a, auto b){};
    // static_assert(std::is_same<GetArg<decltype(f2), 0>, int>{}, "Not the same!");
}

Live

The second lambda with auto specifier does not compile as my spezializations are not matched, because auto is like a template parameter T, which is not known. Is there a way of making this work for f2 as well?

Since the lambda is an opaque type and a template function has no type unless instantiated with template argument types, I have not really any idea of how to make this work? Is it impossible?


Solution

  • I think you miss this:

    template <typename F>
    decltype(getArgs(&F::operator())) getArgs(F);
    

    So final version would be:

    namespace details
    {
    //! Spezialization for Funktion-Pointers
    template <typename Ret, typename... Args>
    std::tuple<Args...> getArgs(Ret (*)(Args...));
    
    //! Spezialization for Functor/Lambdas
    template <typename F, typename Ret, typename... Args>
    std::tuple<Args...> getArgs(Ret (F::*)(Args...));
    
    //! Spezialization for Functor/Lambdas
    template <typename F, typename Ret, typename... Args>
    std::tuple<Args...> getArgs(Ret (F::*)(Args...) const);
    
    template <typename F>
    decltype(getArgs(&F::operator())) getArgs(F);
    
    }; // namespace details
    
    template <typename F, std::size_t I>
    using GetArg = std::tuple_element_t<I, decltype(details::getArgs(std::declval<F>()))>;
    
    

    Now it compiles: https://godbolt.org/z/r1PsE36Wx