Reputation: 9432
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!");
}
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?
Upvotes: 3
Views: 890
Reputation: 11558
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
Upvotes: 1
Reputation: 66200
Is there a way of making this work for
f2
as well?
No, as far I know.
You can see a generic lambda (a lambda with one or more auto
arguments) almost as a template function (wrapped in a class).
Excluding the class wrapping, you can see
[](int a, auto b){};
almost as
template <typename T>
void foo (int a, T b)
{ };
You can't deduce the type of b
from the lambda exactly as you can't deduce the type of b
from foo()
: it's decided calling the function (deducing the type form the argument) or explicating it (something as foo<int>
).
But if you simply write decltype(foo)
, the compiler can't decide which type is T
so gives an error.
Anyway, I get an error also compiling
static_assert(std::is_same<GetArg<decltype(f1), 0>, int>{}, "Not the same!");
I suppose you have to write GetArg
as follows
using GetArg = std::tuple_element_t<I, decltype(details::getArgs(&F::operator()))>;
// ..............................................................^^^^^^^^^^^^^^
Or you can maintain your actual GetArg
but calling it with +f1
static_assert(std::is_same<GetArg<decltype(+f1), 0>, int>{}, "Not the same!");
// ........................................^^^
that is: converting the lambda to a function pointer.
Upvotes: 1