sangrey sangrey - 1 month ago 11
C++ Question

C++ Lambda does not have operator()

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;

AnT AnT
Answer

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.

Comments