1

私はまだいくつかの C++ 構文に苦労しています。
今回は、ラムダに引数を追加したいと思います。しかし、コードを汎用的にするには、任意の関数とその引数を受け入れることができるようにする必要があります。

#include <functional>
#include <exception> 

template<typename R>
class Nisse
{
    private:
        Nisse(Nisse const&)             = delete;
        Nisse(Nisse&&)                  = delete;
        Nisse& operator=(Nisse const&)  = delete;
        Nisse& operator=(Nisse&&)       = delete;
    public:
        //Nisse(std::function<R()> const& func) {}  // disable for testing

        template<typename... Args>
        Nisse(std::function<R(Args...)> const& func, Args... a) {}
};

int main()
{
    // I  was hoping this would deduce the template arguments.
    Nisse<int>   nisse([](int a,double d){return 5;},12,12.0);
}

これにより、次が生成されます。

> g++ -std=c++0x Test.cpp 
Test.cpp:21:61: error: no matching function for call to ‘Nisse<int>::Nisse(main()::<lambda(int, double)>, int, double)’
Test.cpp:21:61: note: candidate is:
Test.cpp:16:9: note: template<class ... Args> Nisse::Nisse(const std::function<R(Args ...)>&, Args ...)

テンプレートの種類を明示的に指定してみました:

    Nisse<int>   nisse<int,double>([](int a,double d){return 5;},12,12.0);

しかし、これは (私には驚くべきことですが) 構文エラーです:

> g++ -std=c++0x Test.cpp 
Test.cpp: In function ‘int main()’:
Test.cpp:21:23: error: expected initializer before ‘&lt;’ token
Test.cpp:21:65: error: expected primary-expression before ‘,’ token
Test.cpp:21:73: error: expected ‘;’ before ‘)’ token
4

1 に答える 1

4

std::functionラムダからテンプレート引数を推測することはできません。任意の callable を受け入れる通常の方法は、ユニバーサル リファレンスによるものです。

    template<typename F, typename... Args,
      typename = typename std::enable_if<std::is_convertible<
        decltype(std::declval<F>()(std::declval<Args>()...)), R>::value>::type>
    Nisse(F &&f, Args... a): Nisse(std::function<R()>(std::bind(f, a...))) {}

ここでは、最後の (匿名でデフォルト設定された) テンプレート引数を使用して、最初のテンプレート引数が関数のようであり、型 R の結果を返すことを検証します。

std::enable_if<std::is_convertible<
    decltype(std::declval<F>()(std::declval<Args>()...)), R>::value>::type

以下の@Yakkによるコメントで説明されているように、上記の式は、Fが結果がRである関数型であることを確認します。それが機能する場合、すべて問題ありません。失敗すると、コンパイル時エラーが発生します (注: これはSFINAEを使用します)。

メソッドの場合、戻り値の型に SFINAE が挿入されますが、コンストラクターの場合、これはオプションではありません。歴史的に、余分なデフォルトのコンストラクター引数が追加されましたが、デフォルトのテンプレート引数を追加すると、コンストラクターのシグネチャがまったく変更されないため、よりエレガントになります。匿名のテンプレート パラメーターによる SFINAE は、ユーザーが (偶然にも?) デフォルトを上書きする方法がないため、可変個引数のテンプレート パラメーターの後に特に魅力的です。

于 2013-02-14T18:00:56.833 に答える