2012-02-11 41 views
2

我正在嘗試爲std容器編寫一個流操作符,主要用於調試目的。is_container trait在std :: set SFINAE問題上失敗

我有以下代碼:

#include <type_traits> 
#include <iostream> 
#include <ostream> 
#include <iterator> 
#include <algorithm> 
#include <functional> 

#include <vector> 
#include <set> 
#include <deque> 


template<typename Container> 
struct is_container 
{ 
    typedef char no; 
    typedef long yes; 


    template<typename A, A, A> 
    struct is_of_type; 

    template<typename T> 
      static yes& is_cont(
          is_of_type 
          < 
            typename T::iterator(T::*)(), 
            &T::begin, 
            &T::end 
          >*); 


    template<typename T> 
    static no& is_cont(...);  //any other 

    template<typename C, bool B> 
    struct is_class_is_container 
    { 
      const static bool value=sizeof(is_cont<C>(nullptr))==sizeof(yes); 
    }; 

    template<typename C> 
    struct is_class_is_container<C, false> 
    { 
      const static bool value=false; 
    }; 

    const static bool value = is_class_is_container 
      < 
        Container, 
        std::is_class<Container>::value 
      >::value; 
}; 

template<typename T> 
    typename std::enable_if 
    < is_container<T>::value, std::ostream >::type& 
    operator<<(std::ostream& os, const T& a) 
{ 
    os << '['; 
    std::copy(a.begin(), a.end(), std::ostream_iterator<typename T::value_type>(os, ", ")); 
    os << ']'; 
    return os; 
} 

我知道這是遠遠不夠完善(有建設性的意見讚賞),但我遇到的問題是,它爲載體,雙端隊列和列表的偉大工程,但未能匹配集合,我不知道爲什麼因爲集合仍然有迭代器接口的開始和結束。

謝謝。

編輯:於 G ++(GCC)4.6.2 2012012 鐺版本測試3.0

EDIT2:我得到了它使用decltype排序的工作不過是次優,因爲現在我不能斷言它我期望的(返回一個迭代器)。

我並不完全知道該設置是什麼返回首先,也許如果有人有調試TMP的方式,這將是一件好事。

+1

我在[漂亮的打印]某種形式的'is_container'特質(http://stackoverflow.com/questions/4850473/pretty-print-c -stl-containers),如果你有興趣。 – 2012-02-11 17:16:56

+0

@KerrekSB謝謝,我會仔細考慮一下,但是,因爲這真的是對我進行這樣的測試,所以我希望能夠做到這一點,並嘗試使其發揮作用。 – 111111 2012-02-11 17:21:20

+0

這看起來不必要。試試這個:http://ideone.com/gBx6P – 2012-02-11 18:17:54

回答

4

由於std::set<T>只有一組不變的迭代器,還有的begin()end()只有一個版本被宣佈爲const。也就是說,std::set<T>定義看起來是這樣的(假設它是宣稱的命名空間std前):

template <typename T> 
class std::set 
{ 
public: 
    class   iterator; 
    typedef iterator const_iterator; 
    ... 
    const_iterator begin() const; 
    const_iterator end() const; 
    ... 
}; 

其他容器同時擁有const和非const版本的begin()end()匹配你的簽名要求。 std::set沒有這個。我不確定最簡單的解決方法是什麼。

也就是說,sizeof(char)允許爲sizeof(long)。最簡單的方法,以保證yesno類型有不同的尺寸是使用不同大小的數組引用爲同一類型,例如:

typedef char (&yes)[1]; 
typedef char (&no)[2]; 
... 
enum { value = sizeof(some_expression) == sizeof(yes) }; 
+0

我當時很接近,我試過陣列的事情,但沒有意識到它必須是一個參考。那更好。此外,我不知道該設置只有常量迭代器。我想我是由這個:http://en.cppreference.com/w/cpp/container/set/begin哦,它現在的作品。我真的不認爲我會打擾我針對c + + 11的枚舉位,所以我沒有東西vc6.0怪異是一個足夠的理由來混淆我的代碼。無論如何,再次感謝。 – 111111 2012-02-11 18:19:13

+0

好了,'enum'位是沒有那麼多「VC6.0」但如果你最終的值傳遞給一些東西,需要值的定義,例如一種必然怪癖一個函數採用'const&':你的'靜態布爾const值= ...'仍然是**不是**的定義,雖然它被賦予了一個初始值!如果您使用的是C++ 2011,您還可以使用'static bool constexpr value = ...'來定義(不只是初始化)類定義中的值(儘管我不完全確定'constexpr'限定的位置去吧,也就是說這個符號可能有點不對)。 – 2012-02-11 18:25:03

+0

「constexpr靜態布爾值」在gcc中工作正常 – 111111 2012-02-11 18:34:48

2

它適用於vector而不是set因爲後者返回const_iteratorbegin/end功能。變化:

typename T::iterator(T::*)(), 

到:

typename T::const_iterator(T::*)() const,