2010-12-03 15 views
7

次のコードは、Tbeginendメソッドを持っているかどうかを検出する必要がありますトラブル1 1 1 1です。ただし、Visual Studio 2008では、出力は1 1 0 0です。私は何か間違ったことをしたのですか、これは単にVS 2008のバグですか?誰か別のコンパイラでテストできますか?ありがとう!SFINAEコンパイラは、鉱山の

+0

( '1 1 1 1 'を得た)のMinGW G ++ 4.4.0に私のために働きました。残念ながら私はVS2008でなぜ失敗するのかわかりませんが、コードは正しいように見えます。 –

+0

VS2010で '1 1 0 0'と同じです。私はSTLをデバッグしているかもしれないが、 '/ DDEBUG'と'/DNDEBUG'を試しましたが、何の違いもありませんでした。 – Rup

+1

Boost.MPLから提供されている['HAS_XXX'ファシリティ](http://live.boost.org/doc/libs/1_34_0/boost/mpl/has_xxx.hpp)を見て、どのように回避できるか見てみましょう特定のコンパイラのSFINAEの容量が制限されています。 –

答えて

1

ステファンT. Lavavejが言ってthisがあります

標準ライブラリのメンバ関数のアドレスを取得することが技術的に禁止されていることに注意してください。(彼らは&foo::barがあいまいなって、過負荷状態にすることができ、それらはstatic_castを経由して明確にする試みを破って、追加のデフォルト引数を持つことができます。)

だから私は、私だけをチェックする簡単なバージョンを使用するつもりだと思います入れ子になったconst_iteratorタイプ。

12

ここでは、これらのことをデバッグする方法について説明します。

まず、否定的な選択肢をコメントアウトして、不一致ではなくエラーになるようにします。 次に、動作しない項目の1つを使用して、関数に入れている型をインスタンス化してみてください。

この手順では、あなたのsfinaeオブジェクトをインスタンス化できましたが、まだ動作していませんでした。これは、それがVSバグであることを私に知らせるので、問題はそれを修正する方法です。

あなたがしているようにVSがSFINAEに問題があるようです。あなたがあなたのsfinaeオブジェクトを包むとき、それはより良く機能します。

template <typename U, typename it_t = typename U::const_iterator > 
struct sfinae 
{ 
    // typedef typename U::const_iterator it_t; - fails to compile with non-cont types. Not sfinae 
    template < typename U, typename IT, IT (U::*)() const, IT (U::*)() const > 
    struct type_ {}; 

    typedef type_<U,it_t,&U::begin,&U::end> type; 
}; 

まだ働いていなかったが、少なくとも私は有益なエラーメッセージました:私はそうなどがあることでした

error C2440: 'specialization' : cannot convert from 'overloaded-function' to 'std::_Tree_const_iterator<_Mytree> (__thiscall std::set<_Kty>::*)(void) const'

をこれは私が&U::endはVSがするには十分ではないことを知ることができますどのエンド()が欲しいかを知ることができます。 static_castは次のように修正しました:

typedef type_<U,it_t,static_cast<it_t (U::*)() const>(&U::begin),static_cast<it_t (U::*)() const>(&U::end)> type; 

VS2010で成功したテストプログラムを実行してください。 static_castは実際に必要なものすべてであるかもしれませんが、私はそれをあなたに残しておきました。

今実際の質問は、どのコンパイラが正しいのでしょうか?私の賭けは一貫していたものです:g ++。

編集:ジェエッシュ...

template <typename T> 
struct is_container 
{ 
    template <typename U, typename it_t = typename U::const_iterator > 
    struct sfinae 
    { 
     //typedef typename U::const_iterator it_t; 
     template < typename U, typename IT, IT (U::*)() const, IT (U::*)() const > 
     struct type_ {}; 

     typedef type_<U,it_t,static_cast<it_t (U::*)() const>(&U::begin),static_cast<it_t (U::*)() const>(&U::end)> type; 
    }; 

    template <typename U> static char test(typename sfinae<U>::type*); 
    template <typename U> static long test(...); 

    enum { value = (1 == sizeof test<T>(0)) }; 
}; 



#include <iostream> 
#include <vector> 
#include <list> 
#include <set> 
#include <map> 

int main() 
{ 
    std::cout << is_container<std::vector<std::string> >::value << ' '; 
    std::cout << is_container<std::list<std::string> >::value << ' '; 
    std::cout << is_container<std::set<std::string> >::value << ' '; 
    std::cout << is_container<std::map<std::string, std::string> >::value << ' '; 
    std::cout << is_container<bool>::value << '\n'; 
} 
+1

@sbi @John @Rup悲しいことに、このコードは動作しません。型が 'begin'または' end'メソッドを持たない場合、型はコンパイルされません。 – fredoverflow

+0

@Fred:しかし、元のコードでも 'begin()'/'end()'をチェックしましたか? (どのクラスも入れ子の 'const_iterator'型を持ちますが、' begin() '/' end() 'は返さないのですか?) – sbi

+3

@sbi:SFINAEは"置換の失敗は** **エラーではありません "を意味します。 'T'が' begin'と 'end'メソッドを持たない場合、' is_container :: value'はまだ**コンパイルされていなければなりません。そして、それはまさに私のコードがg ++で正しく行うことです。 – fredoverflow

5

なぜあなたはすべての努力をしていますか? U::begin()が存在するかどうか確認したい場合は、試してみてください。U::begin()U::end()の存在をチェックをすることに加え

template <typename T> 
struct is_container 
{ 
    template <typename U> static char test(U* u, 
     typename U::const_iterator b = ((U*)0)->begin(), 
     typename U::const_iterator e = ((U*)0)->end()); 
    template <typename U> static long test(...); 

    enum { value = (1 == sizeof test<T>(0)) }; 
}; 

、これはまた、彼らはconst_iteratorに変換可能である何かを返すかどうかをチェックします。 Stephan T. Lavavejによって強調された落とし穴を、特定の署名を前提とするのではなく、サポートしなければならない呼び出し式を使って回避します。

[編集] 申し訳ありませんが、これはVC10のテンプレートのインスタンスに依存していました。より良いアプローチは、(オーバーロードに参加してください引数の型でexistanceチェック、置き):

template <typename T> struct is_container 
{ 
    // Is. 
    template <typename U> 
    static char test(U* u, 
        int (*b)[sizeof(typename U::const_iterator()==((U*)0)->begin())] = 0, 
        int (*e)[sizeof(typename U::const_iterator()==((U*)0)->end())] = 0); 
    // Is not. 
    template <typename U> static long test(...); 

    enum { value = (1 == sizeof test<T>(0)) }; 
}; 
+0

VS2005でテストしましたが、 'is_container > :: value'(' true')の場合でもうまく動作します。 – MSalters

+0

'typename U :: const_iterator b =((U *)0) - > begin()' 'begin()'() ')が呼び出されないという概念を説明してもらえますか? 、ありがとうたくさん:) –

+0

@ Mr.Anubis:デフォルトの引数は、関数が呼び出された時点で評価されます。 'test ()'は実際には呼び出されないので( 'sizeof(expr)は式を評価しません)、デフォルト引数も評価されません。しかし、 'sizeof(テスト(0))'は、戻り値の型を決定するためにオーバーロード解決を行う必要があります。 – MSalters

1

これはおそらくする必要がありますコメントを、私は十分なポイント

@MSalters

を持っていませんあなたの is_container作品は(ほとんど)と私はあなたのコードを自分自身を使用していたにもかかわらず

が、私はそれに2つの問題を発見しました。

まずタイプdeque<T>::iteratorが(GCC-4.7で)コンテナとして検出されることです。 deque<T>::iteratorbegin/endメンバーと定義されてconst_iteratorタイプを持っているようです。

第2の問題は、このコードは、GCCの開発者によれば無効であることです。私qoute:デフォルトの引数の値は、関数型の一部ではなく、控除には含まれません。私は現在、is_container<T>のために(C++ 11のみ)thisを使用していますGCC bug 51989

を参照してください:C++ 11で

template <typename T> 
struct is_container { 
    template < 
     typename U, 
     typename S = decltype (((U*)0)->size()), 
     typename I = typename U::const_iterator 
    > 
    static char test(U* u); 
    template <typename U> static long test(...); 
    enum { value = sizeof test<T>(0) == 1 }; 
}; 
+0

'deque'はコンテナではありませんか? – fredoverflow

+0

'deque'はコンテナですが、' deque :: iterator'はコンテナではありません。 –

+0

第2の問題は本当です。私は14.7.1/2を見落とした。デフォルトの引数は、テンプレート自身でインスタンス化されません。 – MSalters

3

、これを検出するためのより良い方法が存在することになります。代わりに、関数のシグネチャに頼るので、私たちは単に表現SFINAEコンテキストでそれらを呼び出す:

#include <type_traits> // declval 

template<class T> 
class is_container{ 
    typedef char (&two)[2]; 

    template<class U> // non-const 
    static auto test(typename U::iterator*, int) 
     -> decltype(std::declval<U>().begin(), char()); 

    template<class U> // const 
    static auto test(typename U::const_iterator*, long) 
     -> decltype(std::declval<U const>().begin(), char()); 

    template<class> 
    static two test(...); 

public: 
    static bool const value = sizeof(test<T>(0, 0)) == 1; 
}; 

Live example on Ideone.intlongパラメータは、コンテナの両方を提供しています(またはiteratortypedef const_iterator iteratorであれば時にオーバーロードの解決を明確にするだけであり、 std::setのように) - 0のタイプはintであり、最初の過負荷が選択されます。

関連する問題