5

ファンクターがあると仮定します。

struct MyFunctor
{
    bool operator ()( int value )
    {
        return true;
    }
};

テンプレート内で使用するためにファンクターのメンバーの引数タイプを取得することは可能ですか?以下は、この神話上の機能の使用法です。

template < typename FunctorType >
bool doIt( FunctorType functor, typename FunctorType::operator()::arg1 arg )
{
    return functor( arg );
}

私の神話の代わりになる有効な構文はありますFunctorType::operator()::arg1か?

4

4 に答える 4

7

アイテムがファンクターであることがわかっている場合は、次のように、アイテムを取得できますoperator()

#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());
}

私の場合、そのプログラムは次のように出力されます。

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*]

重要なのは、それぞれとがとArg0であるということです。Arg1longint*

于 2012-05-01T15:40:01.443 に答える
4

いいえ、ありません。これを行う最もエレガントな方法は、ファンクターにtypedef引数タイプのを提供するように要求するか、特性クラスを導入することです。後者は、テンプレートをファンクターと関数で機能させる場合に役立ちます。

または、引数の型を2番目のテンプレートパラメータにすることもできます。

template < typename FunctorType, class ArgumentType >
bool doIt( FunctorType functor, ArgumentType arg )
{
    return functor( arg );
}

ArgumentTypeファンクターに必要なタイプと一致しない場合でも、コンパイラーは文句を言います。

于 2011-07-12T16:16:47.270 に答える
3

あなたは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...);
}
于 2011-07-12T16:37:28.567 に答える
2

ここでは、@BjörnPollex(正解)の回答にC++11アップデートを提供します。

質問に戻ると、doIt主に渡すことができるものを制限するために、の2番目の引数を明示的に指定する必要があります。C ++ 11では、ファンクターの引数タイプを明示的に知らなくても、この制限を暗示することができます(ファンクターがオーバーロードされた場合、これは明確に定義されていません)。

template < typename FunctorType, class ArgumentType >
auto doIt( FunctorType functor, ArgumentType arg ) -> decltype(bool(functor(arg)))
{
    return functor( arg );
}

(への変換はbool必要ないかもしれませんが、リターンタイプを本当に必要としているように見えるので、ここに配置しますbool)。

このdoIt(テンプレート)関数は、引数と互換性があるfunctor(およびに変換可能な)引数を取りますbool。渡された引数に互換性がない場合、関数はまったく存在せず、エレガントな「doIt関数が見つかりません」コンパイラエラーが発生します。

doItパーフェクトフォワードを使用して、次と完全に同等にすることで、さらに一歩進むことができます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) );
}
于 2014-01-25T18:54:46.507 に答える