开发者

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

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 f开发者_StackOverflowunctor( arg );
}

Is there a valid syntax that would substitute for my mythical FunctorType::operator()::arg1 ?


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.


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.


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) );
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜