Is it possible to retrieve the argument types from a (Functor member's) function signature for use in a template?

老子叫甜甜 提交于 2019-12-04 09:20:35

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) );
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!