9

SFINAEの問題があります。

次のコードでは、C++ コンパイラに特殊化されたファンクターを選択して "special" を出力させたいのですが、代わりに "general" を出力しています。

#include <iostream>
#include <vector>

template<class T, class V = void>
struct Functor {
  void operator()() const {
    std::cerr << "general" << std::endl;
  }
};

template<class T>
struct Functor<T, typename T::Vec> {
  void operator()() const {
    std::cerr << "special" << std::endl;
  }
};

struct Foo {
  typedef std::vector<int> Vec;
};

int main() {
  Functor<Foo> ac;
  ac();
}

特殊な構造体が自動的に使用されるように修正するにはどうすればよいですか? Functorstructを に直接特化したくないことに注意してくださいFoo。しかし、型を持つすべての型に特化したいのですVec

PS: g++ 4.4.4 を使用しています

4

3 に答える 3

12

最後の答えであなたを誤解させて申し訳ありませんが、私はそれがもっと簡単だろうと少しの間思いました。そこで、ここで完全なソリューションを提供しようと思います。このタイプの問題を解決するための一般的なアプローチは、トレイトenable_ifヘルパーテンプレートを作成し、それを(C ++ 11、ブースト、または手動実装のいずれか)と一緒に使用して、クラスの特殊化を決定することです。

特性

単純なアプローチは、必ずしも最良ではありませんが、書くのは簡単です。

template <typename T>
struct has_nested_Vec {
    typedef char yes;
    typedef char (&no)[2];
    template <typename U>
    static yes test( typename U::Vec* p );
    template <typename U>
    static no test( ... );

    static const bool value = sizeof( test<T>(0) ) == sizeof(yes);
};

アプローチは単純で、異なるサイズのタイプを返す2つのテンプレート関数を提供します。1つはネストされたVecタイプを取り、もう1つは省略記号を取ります。ネストされたすべてのタイプの場合Vec、最初のオーバーロードがより適切に一致します(省略記号は、どのタイプに対しても最悪の一致です)。VecネストされたSFINAEがないタイプの場合、そのオーバーロードは破棄され、残っているオプションは省略記号のみになります。これで、どのタイプにもネストされたVecタイプがあるかどうかを尋ねる特性ができました。

次の場合に有効にする

これはどのライブラリからでも使用できますが、独自のライブラリを使用することもできます。非常に簡単です。

template <bool state, typename T = void>
struct enable_if {};

template <typename T>
struct enable_if<true,T> {
    typedef T type;
};

最初の引数がfalse、の場合、ベーステンプレートが唯一のオプションであり、ネストされたものはありませんtype。条件がの場合、trueSFINAEで使用できるenable_ifネストされたものがあります。type

実装

次に、ネストされたタイプのみにSFINAEを使用するテンプレートと特殊化を提供する必要がありますVec

template<class T, class V = void>
struct Functor {
    void operator()() const {
        std::cerr << "general" << std::endl;
    }
};
template<class T>
struct Functor<T, typename enable_if<has_nested_Vec<T>::value>::type > {
    void operator()() const {
        std::cerr << "special" << std::endl;
    }
};

型を使用してインスタンス化するときは常にFunctor、コンパイラーは特殊化を使用しようとします。これhas_nested_Vecにより、に渡される真理値がインスタンス化されて取得されenable_ifます。値がであるタイプの場合falseenable_ifネストされたtypeタイプがないため、特殊化はSFINAEで破棄され、ベーステンプレートが使用されます。

あなたの特定のケース

タイプ全体を特殊化する必要はなく、演算子だけを特殊化する必要があると思われる特定のケースでは、3つの要素を1つの要素に混合できます。これはFunctor、プレゼンスに基づいて2つの内部テンプレート関数の1つにディスパッチします。の必要性と特性クラスVecを削除します。enable_if

template <typename T>
class Functor {
   template <typename U>
   void op_impl( typename U::Vec* p ) const {
      std::cout << "specialized";
   }
   template <typename U>
   void op_impl( ... ) const {
      std::cout << "general";
   }
public:
   void operator()() const {
      op_impl<T>(0);
   }
};
于 2012-07-04T02:26:58.113 に答える
4

これは古い質問ですが、元のコードをすばやく修正するための代替手段をいくつか提供する価値があると思います。

基本的に、問題は SFINAE の使用 (実際にはその部分で問題ありません) ではなく、プライマリ テンプレート ( void) の既定のパラメーターと部分的な特殊化 ( typename T::Vec) で提供される引数の一致にあります。プライマリ テンプレートのデフォルト パラメータのため、Functor<Foo>実際には を意味しFunctor<Foo, void>ます。コンパイラが特殊化を使用してそれをインスタンス化しようとすると、2 つの引数を特殊化の引数と一致させようとし、 をvoid置き換えることができないため失敗しますstd::vector<int>。その後、プライマリ テンプレートを使用したインスタンス化にフォールバックします。

Vecしたがって、すべての がであると仮定する最も簡単な修正はstd::vector<int>、行を置き換えることです

template<class T, class V = void>

これとともに

template<class T, class E = std::vector<int>>

引数が一致するため、特殊化が使用されます。シンプルですが、制限が多すぎます。明らかに、プライマリ テンプレートでデフォルト パラメータとして指定できるものと一致させるために、特殊化で引数の型をより適切に制御する必要があります。新しい特性を定義する必要のない 1 つの簡単な解決策は次のとおりです。

#include <iostream>
#include <vector>
#include <type_traits>

template<class T, class E = std::true_type>
struct Functor {
  void operator()() const {
    std::cerr << "general" << std::endl;
  }
};

template<class T>
struct Functor<T, typename std::is_reference<typename T::Vec&>::type> {
  void operator()() const {
    std::cerr << "special" << std::endl;
  }
};

struct Foo {
  typedef std::vector<int> Vec;
};

int main() {
  Functor<Foo> ac;
  ac();
}

これはVec、基本的な型や配列、それらへの参照やポインターなど、ここで意味をなすすべての型で機能します。

于 2014-12-04T23:38:21.357 に答える