25

名前の付いた関数は、そのパラメーターとしてtest受け取ります。std::function<>

template<typename R, typename ...A>
void test(std::function<R(A...)> f)
{
    // ...
}

しかし、私が次のことをすると:

void foo(int n) { /* ... */ }

// ...

test(foo);

コンパイラ(gcc 4.6.1)は言いno matching function for call to test(void (&)(int))ます。

最後の行test(foo)をコンパイルして正しく機能させるには、関数を変更するにはどうすればよいtest()ですか?test()関数では、fタイプが必要ですstd::function<>

つまり、コンパイラに関数のシグネチャを決定させ(例)、それを自動的fooに変換するためのテンプレートトリックはありますか?std::function<void(int)>

編集

これをラムダ(ステートレスとステートレスの両方)でも機能させたいと思います。

4

4 に答える 4

14

オーバーロードを使用したいようです

template<typename R, typename ...A>
void test(R f(A...))
{
    test(std::function<R(A...)>(f));
}

この単純な実装は、渡そうとするすべてではないにしてもほとんどの関数を受け入れます。エキゾチックな機能は拒否されます(のようにvoid(int...))。より多くの作業はあなたにより多くの一般性を与えるでしょう。

于 2012-02-11T17:19:22.843 に答える
10

std::functionCallableインターフェースを実装します。つまり、関数のように見えますが、呼び出し可能オブジェクトがsである必要があるという意味ではありませんstd::function

template< typename F > // accept any type
void test(F const &f) {
    typedef std::result_of< F( args ) >::type R; // inspect with traits queries
}

ダックタイピングは、テンプレートメタプログラミングの最良のポリシーです。テンプレート引数を受け入れるときは、具体的ではなく、クライアントにインターフェイスを実装させるだけです。

たとえば、変数またはそのようなクレイジーなものを再ターゲットする必要がありstd::function、入力が生の関数ポインターであることがわかっている場合は、生の関数ポインター型を分解して、に再構成できますstd::function

template< typename R, typename ... A >
void test( R (*f)( A ... ) ) {
    std::function< R( A ... ) > internal( f );
}

std::functionこれで、関数内にカプセル化されているため、ユーザーはを渡すことができません。既存のコードを別のオーバーロードとして保持し、それに委任することもできますが、インターフェイスを単純に保つように注意してください。

ステートフルラムダに関しては、その場合の処理​​方法がわかりません。それらは関数ポインタに分解されず、私が知る限り、引数の型を照会したり推測したりすることはできません。この情報はstd::function、良くも悪くも、インスタンス化するために必要です。

于 2012-02-12T14:16:22.810 に答える
4

std::function「バイナリ区切り」(動的ライブラリ、「不透明」API など) でない限り、通常、値で受け入れることはお勧めできません。関数が実際にstd::functionby 値を取る場合、(関数がまったくオーバーロードされている場合) オーバーロードの問題を回避するために、オブジェクトを作成するのは呼び出し元の負担になることがよくあります。

std::functionただし、テンプレートを作成したため、型消去の利点のために (パラメーター型として) を使用していない可能性があります。やりたいことが任意のファンクターを検査することである場合、そのためのいくつかの特性が必要です。たとえば、Boost.FunctionTypes には や などの特性がresult_typeありparameter_typesます。最小限の機能的な例:

#include <functional>

#include <boost/function_types/result_type.hpp>
#include <boost/function_types/parameter_types.hpp>
#include <boost/function_types/function_type.hpp>

template<typename Functor>
void test(Functor functor) // accept arbitrary functor!
{
    namespace ft = boost::function_types;

    typedef typename ft::result_type<Functor>::type result_type;
    typedef ft::parameter_types<Functor> parameter_types;
    typedef typename boost::mpl::push_front<
        parameter_types
        , result_type
    >::type sequence_type;
    // sequence_type is now a Boost.MPL sequence in the style of
    // mpl::vector<int, double, long> if the signature of the
    // analyzed functor were int(double, long)

    // We now build a function type out of the MPL sequence
    typedef typename ft::function_type<sequence_type>::type function_type;

    std::function<function_type> function = std::move(functor);
}

最後に、多相ファンクターでは機能しないため、一般的なケースではファンクターの内省 (つまり、結果の型と引数の型を調べること) はお勧めしません。いくつかのオーバーロードを検討してくださいoperator()。その場合、「正規の」結果タイプまたは引数タイプはありません。C++11 では、任意の種類のファンクターを「積極的に」受け入れるか、SFINAE などの手法を使用して、またはstatic_assert必要に応じてそれらを制約し、後で (パラメーターが利用可能な場合)特定のセットstd::result_ofの結果の型を検査するために使用することをお勧めします。引数の. 前もって制約することが望ましいのは、目的がファンクタをたとえば のコンテナに格納することである場合ですstd::function<Sig>

前の段落の意味を理解するには、上記のスニペットをポリモーフィック ファンクターでテストするだけで十分です。

于 2012-02-12T07:52:38.150 に答える