2012-03-02 16 views
27

https://stackoverflow.com/a/1967183/134841では、部材はおそらくベースクラスで、存在確認、溶液は静的メンバはおそらくタイプのサブクラスでは、存在するかどうかを確認するために設けられていますC++ 11 finalクラスでは動作しません。これは、テスト対象のクラスから継承するためです(final)。C++ 11バージョン

OTOH、この1:

template <typename C> 
struct has_reserve_method { 
private: 
    struct No {}; 
    struct Yes { No no[2]; }; 
    template <typename T, typename I, void(T::*)(I) > struct sfinae {}; 
    template <typename T> static No check(...); 
    template <typename T> static Yes check(sfinae<T,int, &T::reserve> *); 
    template <typename T> static Yes check(sfinae<T,size_t,&T::reserve> *); 
public: 
    static const bool value = sizeof(check<C>(0)) == sizeof(Yes) ; 
}; 

はbaseclassesでreserve(int/size_t)方法を見つけることができません。両方がTのbaseclassesにreserved()を見つけ、Tfinalであればまだ動作し、このメタ関数の実装は

ありますか?

+3

C++ 11では、C++ 03に必要なこのラウンドアバウトの方法の代わりに、 "式のためのsfinae"を使用することができます。 –

+1

@ JohannesSchaub-litb解決を求める!私は、このような例の違いを見たいと思っています。 – stinky472

答えて

46

実際には、decltypeとレイトリターンバインディング機構のおかげで、C++ 11ではもっと楽になっています。

今、それがこのテストにメソッドを使用するだけで簡単です:あなたはその後、例えば、クラスでこれを使用することができます

// Culled by SFINAE if reserve does not exist or is not accessible 
template <typename T> 
constexpr auto has_reserve_method(T& t) -> decltype(t.reserve(0), bool()) { 
    return true; 
} 

// Used as fallback when SFINAE culls the template method 
constexpr bool has_reserve_method(...) { return false; } 

template <typename T, bool b> 
struct Reserver { 
    static void apply(T& t, size_t n) { t.reserve(n); } 
}; 

template <typename T> 
struct Reserver <T, false> { 
    static void apply(T& t, size_t n) {} 
}; 

をそして、あなたはそれを使用します。

template <typename T> 
bool reserve(T& t, size_t n) { 
    Reserver<T, has_reserve_method(t)>::apply(t, n); 
    return has_reserve_method(t); 
} 

enable_ifの方法を選択することもできます。

template <typename T> 
auto reserve(T& t, size_t n) -> typename std::enable_if<has_reserve_method(t), bool>::type { 
    t.reserve(n); 
    return true; 
} 

template <typename T> 
auto reserve(T& t, size_t n) -> typename std::enable_if<not has_reserve_method(t), bool>::type { 
    return false; 
} 

これは実際には簡単ではないことに注意してください。一般的に、それははるかに簡単だだけSFINAEが存在する場合 - あなたはちょうどenable_if 1つの方法にしたいと任意のフォールバックを提供していない:置換が失敗した場合

template <typename T> 
auto reserve(T& t, size_t n) -> decltype(t.reserve(n), void()) { 
    t.reserve(n); 
} 

、この方法は、可能な過負荷のリストから削除されます。

注:,(カンマ演算子)の意味のおかげで、decltypeに複数の式を連鎖させることができ、最後の式だけが実際に型を決定します。複数の操作をチェックするのに便利です。私が指し示すしたい

template<typename> struct Void { typedef void type; }; 

template<typename T, typename Sfinae = void> 
struct has_reserve: std::false_type {}; 

template<typename T> 
struct has_reserve< 
    T 
    , typename Void< 
     decltype(std::declval<T&>().reserve(0)) 
    >::type 
>: std::true_type {}; 

+0

'' const'''関数のパラメータになるために 'T'はリテラル型である必要はありませんか?テンプレートでは、 'constexpr'はインスタンス化時に' constexpr'にはできませんが、テンプレート引数リストで使用しています。 –

+0

@Marc:いいえ、何がパラメータになることができるかにほとんど制限はありません。 – Xeo

+0

これは私に正しい方向を与えました。連続した単純化の後で、第2のC++ 98バージョンの構造を維持することによって最小限のC++ 11サポート(単に 'decltype')を必要とするが、' Yes'バージョンを単一の 'テンプレート static decltype (static_cast (0) - > isNull()、Yes())チェック(int); –

10

[とにかく実際には非問題であり、ヨハネスコメントを見る]もdecltypeではなく(...)に、任意の種類を渡すに依存しているバージョンstd::vector<int>&のようなタイプはこのタイプに応じてreserveをサポートしています:ここでは式は検査されていますが、タイプは検査されていません。この特性が答える質問は、「このような型のlvalが与えられているとTの場合、式はlval.reserve(0);となります。 "このタイプまたはその基本タイプのいずれかに、reserveのメンバーが宣言されていますか?"という質問と同じではありません。

一方、それは間違いなく機能です!新しいC++ 11の特性は、is_default_constructible,ではなく、has_default_constructorのスタイルであることに注意してください。その区別は微妙ですが、メリットがあります。 (is_*ibleのスタイルでより良いフィッティング名を練習として残しておきます)

いずれにしても、std::is_classのような形質を使用して、必要なものを達成することができます。

+0

これは美しく動作します、ありがとう! – Avio