2012-08-28 89 views

回答

11

遺憾的是,沒有標準的方法來檢測一個班級是否模型Iterator。最簡單的檢查是*it++it在語法上都是有效的;

template<typename T, 
    typename = decltype(*std::declval<T&>(), void(), ++std::declval<T&>(), void())> 
    MyClass(T); 

考慮從24.2.2的Iterator要求:2:您可以使用標準SFINAE技術做到這一點

template<typename T> typename std::enable_if< 
    !std::is_void<decltype(*std::declval<T &>())>::value 
    && std::is_same<decltype(++std::declval<T &>()), 
        typename std::add_lvalue_reference<T>::type>::value, 
    std::true_type>::type has_iterator_requirements_helper(int); 
template<typename T> std::false_type has_iterator_requirements_helper(...); 
template<typename T> struct has_iterator_requirements: 
    decltype(has_iterator_requirements_helper<T>(0)) {}; 

template<typename, bool> struct is_iterator_check: std::false_type {}; 
template<typename T> struct is_iterator_check<T, true>: std::true_type { 
    typedef typename std::iterator_traits<T>::difference_type difference_type; 
    typedef typename std::iterator_traits<T>::value_type value_type; 
    typedef typename std::iterator_traits<T>::iterator_category iterator_category; 
    typedef typename std::iterator_traits<T>::reference reference; 
    typedef typename std::iterator_traits<T>::pointer pointer; 
    static_assert(std::is_same<decltype(*std::declval<T &>()), reference>::value 
     || std::is_void<reference>::value, "*r must be of type reference"); 
}; 
template<typename T> struct is_iterator: is_iterator_check<T, 
    (std::is_pointer<T>::value 
    && !std::is_void<typename std::remove_pointer<T>::type>::value 
    && !std::is_function<typename std::remove_pointer<T>::type>::value 
    ) || (std::is_copy_constructible<T>::value 
    && std::is_copy_assignable<T>::value 
    && std::is_nothrow_destructible<T>::value 
    // TODO: check lvalues are swappable 
    && has_iterator_requirements<T>::value 
    )> {}; 

與嘗試使用iterator_traits的是,它是所有定義模板的問題類型,並且其實例在非SFINAE上下文中將失敗(回想SFINAE僅適用於直接替換失敗)。 libstdC++有conforming extension,因此在非迭代器類型上實例化iterator_traits將產生一個空類型;您可以通過檢查的iterator_category的類型存在做類似的伎倆:

template<typename T> std::true_type has_iterator_category_helper(
    T::iterator_category *); 
template<typename T> std::false_type has_iterator_category_helper(...); 
template<typename T> struct has_iterator_category<T>: 
    decltype(has_iterator_category_helper<T>(0)) { }; 
template<typename T> struct is_iterator: std::integral_constant<bool, 
    std::is_pointer<T>::value || has_iterator_category<T>::value> {}; 

template<typename T, typename = std::enable_if<is_iterator<T>::value>> 
    MyClass(T); 

然而,這將不會爲不暴露自己iterator_category但已經適應由獨立iterator_traits專業化工種;在這種情況下,簡單的SFINAE方法更有意義(並且您可以在構造函數中實例化iterator_traits以確認該類型是類似於迭代器的)。

+0

@LucDanton同意,我把SFINAE作爲首選技術。 – ecatmur

+5

偉大的答案,你救了我解釋我們在libstdC++中使用的擴展:)關於該擴展的背景,請參閱http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40497。檢查'iterator_category'的想法歸功於Alisdair Meredith。 –

+0

Mmmh,'* it'的要求是該類型是'std :: iterator_traits :: reference';不是它是一個引用類型(至少對於Iterator而言)。但是你不能使用'std :: iterator_traits'來擔心搞亂SFINAE ......我會讓你修復那個! (你測試的某些表達式的值類別也有問題;例如'++ std :: declval ()',而不是'T'。) –