Assume you have a functor:
struct MyFunctor
{
bool operator ()( int value )
{
return true;
}
};
Is it possible to retrieve a functor's member's argument type for use within your template? The following is a use of this mythical functionality:
template < typename FunctorType >
bool doIt( FunctorType functor, typename FunctorType::operator()::arg1 arg )
{
return functor( arg );
}
Is there a valid syntax that would substitute for my mythical FunctorType::operator()::arg1
?
No there is not. The most elegant way to do this would be to either require your functors to provide a typedef
for the argument-type, or to introduce a traits-class. The latter is useful if you want your template to work with functors and functions.
Alternatively, you can just make the argument type a second template parameter:
template < typename FunctorType, class ArgumentType >
bool doIt( FunctorType functor, ArgumentType arg )
{
return functor( arg );
}
The compiler will still complain if ArgumentType
does not match the type required by the functor.
If you know the item is a functor, then you can just grab its operator()
, like so:
#include <iostream>
template <unsigned Idx, typename... T>
struct pick
{
static_assert(Idx < sizeof...(T), "cannot index past end of list");
};
template <typename T, typename... TRest>
struct pick<0U, T, TRest...>
{
typedef T result;
};
template <unsigned Idx, typename T, typename... TRest>
struct pick<Idx, T, TRest...>
{
typedef typename pick<Idx-1, TRest...>::result result;
};
template <typename Func>
struct func_traits;
template <typename TObj, typename R, typename... TArgs>
struct func_traits<R (TObj::*)(TArgs...)>
{
typedef R result_type;
template <unsigned Idx>
struct argument
{
typedef typename pick<Idx, TArgs...>::result type;
};
};
template <typename Func,
typename Traits = func_traits<Func>,
typename R = typename Traits::result_type,
typename Arg0 = typename Traits::template argument<0>::type,
typename Arg1 = typename Traits::template argument<1>::type
>
void foo(Func f)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
};
struct thing
{
void operator()(long, int*) { }
};
int main()
{
foo(&thing::operator());
}
For me, that program prints out:
void foo(Func) [with Func = void (thing::*)(long int, int*), Traits = func_traits<void (thing::*)(long int, int*)>, R = void, Arg0 = long int, Arg1 = int*]
The key point being that Arg0
and Arg1
are long
and int*
, respectively.
You can sort of do it in C++0x
template <typename... Args>
struct Function {
typedef std :: tuple <Args...> args;
void call () (Args... args);
}
template <typename... Args>
void do_it (Function<Args...>::args:: SOMETHING :: type t, Args... args) {
something (t)
Function <Args...> :: call (args...);
}
Here I give a C++11 update to @BjörnPollex (correct) answer.
Going back the question, you want to specify the second argument of doIt
explicitly mainly to restrict what can be passed. In C++11 you can imply this restriction without knowing explicitly the argument type of the functor (which is not well defined if the functor overloaded anyway).
template < typename FunctorType, class ArgumentType >
auto doIt( FunctorType functor, ArgumentType arg ) -> decltype(bool(functor(arg)))
{
return functor( arg );
}
(the conversion to bool
may not be even necessary, I put it here because it seem that you really want the return type to be bool
).
This doIt
(template) function will take any argument that is possibly compatible with a functor
argument (and also convertible to bool
). If the argument passed is not compatible the function will not even exist at all, and will produce an elegant "doIt function not found" compiler error.
One can go one step more by using perfect forward to make doIt
exactly equivalent to functor(arg)
:
template < typename F, class A >
auto doIt( F&& f, A&& a ) -> decltype(bool(std::forward<F>(f)(std::forward<A>(a))))
{
return std::forward<F>(f)( std::forward<A>(a) );
}
来源:https://stackoverflow.com/questions/6667449/is-it-possible-to-retrieve-the-argument-types-from-a-functor-members-function