I need a method of figuring out a function's argument types, and so I wrote a closure_traits class, given below, as inspired by Is it possible to figure out the parameter type and return type of a lambda?.
However, when I try to apply it to a simple lambda, I get the error that 'operator()' is not a member of '(lambda type)'. However, according to cppreference, lambda's do have an operator(). I also tried using std::function, and got the equivalent error. I guess I'm not sure what's going wrong, and any help would be greatly appreciated.
#include<type_traits>
#include<tuple>
#include<utility>
#include<iostream>
/* For generic types use the type signature of their operator() */
template <typename T>
struct closure_traits : public
closure_traits<decltype(&T::operator())> {};
/* Otherwise, we do a template match on a function type. */
template <typename ClassType, typename ReturnType,
typename... ArgTypes>
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args)>
{
using arity = std::integral_constant<std::size_t,
sizeof...(ArgTypes)>;
using Ret = ReturnType;
/* The argument types will be the same as the types of the
* elements of a tuple composed of them.
*/
template <std::size_t I>
struct Args {
using type = typename std::tuple_element<I,
std::tuple<ArgTypes...>>::type;
};
};
int main() {
auto thing = [=] (int x) {return x;};
std::cerr << "The number of arguments is "
<< closure_traits<decltype(thing)>::arity << std::endl;
return 0;
}
The compiler error messages that I get are below. My compile command is simply g++ -std=c++14 main.cpp.
main.cpp: In instantiation of ‘struct closure_traits<int (main()::<lambda(int)>::*)(int) const>’:
main.cpp:9:8: required from ‘struct closure_traits<main()::<lambda(int)> >’
main.cpp:34:82: required from here
main.cpp:9:56: error: ‘operator()’ is not a member of ‘int (main()::<lambda(int)>::*)(int) const’
struct closure_traits : public closure_traits<decltype(&T::operator())> {};
^
main.cpp: In function ‘int main()’:
main.cpp:34:51: error: ‘arity’ is not a member of ‘closure_traits<main()::<lambda(int)> >’
std::cerr << "The number of arguments is " << closure_traits<decltype(thing)>::arity << std::endl;
Your specialization does not match the decltype(&T::operator())
argument.
Because of this, instead of choosing the specialization (as you wanted it to), the compiler is forced to recursively choose the same main template. Which makes it to apply &T::operator()
expression again, after it was already applied once. I.e the initial attempt to do &T::operator()
actually succeeds, but then the compiler attempts to apply &T::operator()
again, when T
is already int (main()::<lambda(int)>::*)(int) const
. The latter, obviously, does not have operator ()
, which is why you are getting this error message.
The reason it cannot choose your specialization is missing const
in template parameter declaration. The lambda's operator ()
is actually a const
member of the lambda class. Add const
to the declaration of your specialization
template <typename ClassType, typename ReturnType,
typename... ArgTypes>
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args) const>
...
an the compiler will follow the specialization path you intended it to follow.
And, of course, you have to print closure_traits<decltype(thing)>::arity::value
, not just closure_traits<decltype(thing)>::arity
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With