2010-12-02 8 views
8
template<class T> 
struct is_iterator 
{ 
    static const bool value = ??? // What to write ??? 
}; 

int main() 
{ 
    assert(false == is_iterator<int>::value); 
    assert(true == is_iterator<vector<int>::iterator>::value); 
    assert(true == is_iterator<list<int>::iterator>::value); 
    assert(true == is_iterator<string::iterator>::value); 
    assert(true == is_iterator<char*>::value); // a raw pointer is also an iterator 
} 

質問があります:5つのアサート文をパスするにはどうすればいいですか?テンプレートパラメータがイテレータの型であるかどうかをチェックする方法は?

+0

私はコンセプトチェックはあなたを助けることができます。しかし、それは簡単ではない。 –

+0

あなたは本当に知りたい**が必要ですか?あなたが見つけたときに何をするつもりですか?型が演算子*と演算子++を実装していることをテストするだけで十分でしょうか? –

+0

@Karl Knechtel:私の目的は、型Tに演算子*と演算子++(組み込み演算子を含む)がある場合、is_iterator :: valueはゼロではありません。 – xmllmx

答えて

6
template<class T> 
struct is_iterator 
{ 
    static T makeT(); 
    typedef void * twoptrs[2]; // sizeof(twoptrs) > sizeof(void *) 
    static twoptrs & test(...); // Common case 
    template<class R> static typename R::iterator_category * test(R); // Iterator 
    template<class R> static void * test(R *); // Pointer 

    static const bool value = sizeof(test(makeT())) == sizeof(void *); 
}; 
+0

"|| is_pointer :: value"を使用する必要があるのはなぜですか? " static void * test(R *); // Pointer"という行を最初にチェックするだけで十分ではありませんか? –

+0

@Jan de Vos。私はArmenが私の答えを変えた理由を知らない。 –

+3

イテレータ(関数型など)ではないいくつかのものに対してコンパイルに失敗し、イテレータでないいくつかのものに対してtrueを返します(関数へのポインタなど)。関数の配列関数へのポインタをインクリメントすることはできません)。しかし、ほとんどの実用的な目的のために十分に近いと思います。私は、イテレータであるものに対してもfalseを返すことができますが、ポインタでもなく、iterator_type型のtypedefも持っていないと考えています。 'std'のイテレータは単に' std :: iterator_traits'を特化することが許されていると思います。 –

2

さて、あなたはこれがSFINAEを使用して行うことができ、かつ正確な技術がwiki page for SFINAEで見つけることができますiterator_categoryと呼ばれる入れ子になったのtypedefを持っているタイプをチェックすることができます。これは100%メソッドではありませんが、すべてのまともなイテレータはイテレータに共通のtypedefを提供する必要があり、iterator_categoryはイテレータ固有のものです。 TYPEが単にポインタであるかどうかを確認することも忘れないでください。ポインタはイテレータです。数年後、ここに来て

+0

私は数時間試してみましたが、少なくともVC++ 2010ではそれほど問題ではないことがわかりました。 – xmllmx

+0

これは簡単ではありませんが、実行可能です –

+0

@Armen Tsirunyan:私は多くの方法で試してみると成功しません。助けてください。 – xmllmx

1
template < class T, class Enabler = void > 
struct is_iterator : public boost::false_type { }; 

template < class T > 
struct is_iterator< T, typename boost::enable_if_c< 
     sizeof(*(*(T*)0)) + sizeof((*(T*)0)++) + sizeof(++(*(T*)0)) + 
     sizeof((*(T*)0) == (*(T*)0)) + sizeof((*(T*)0) != (*(T*)0)) + 
     sizeof((*(T*)0) = (*(T*)0)) >::type > : public boost::true_type { }; 
+0

Tが逆参照可能でない場合、テストは機能しません。 a intまたは同様のもの – metamorphosis

7

、ここで、C++ 11とC++ 14には、そのような事をすることが多く容易になります。 iteratorは、その核心で、逆参照可能であり、増分可能なものである。それがinput iteratorであれば、それにも匹敵します。あなたが望むように見えるので、後者と一緒に行きましょう。

最も単純なバージョンはvoid_tを使用することであろう。

template <typename... > 
using void_t = void; 

ベースケース:

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

有効なケースの特殊:

template <typename T> 
struct is_input_iterator<T, 
    void_t<decltype(++std::declval<T&>()),      // incrementable, 
      decltype(*std::declval<T&>()),      // dereferencable, 
      decltype(std::declval<T&>() == std::declval<T&>())>> // comparable 
    : std::true_type { }; 

別名:

template <typename T> 
using is_input_iterator_t = typename is_input_iterator<T>::type; 

iterator_categoryに頼るか、過負荷解決を使用して面倒なC++ 03スタイルのチェックを使用する必要はありません。表現SFINAEはここにあります。


氏Wakelyコメントで指摘したように、[iterator.traits]ことを必要とする:タイプ

Iterator場合は イテレータのタイプであることが要求される

iterator_traits<Iterator>::difference_type 
iterator_traits<Iterator>::value_type 
iterator_traits<Iterator>::iterator_category 

は、反復子の差異タイプ、値タイプ、および反復子カテゴリとしてそれぞれ定義されます。

だから我々は単にそれをチェックするために、当社のイテレータ形質を定義することができます。

template <class T, class = void> 
struct is_iterator : std::false_type { }; 

template <class T> 
struct is_iterator<T, void_t< 
    typename std::iterator_traits<T>::iterator_category 
>> : std::true_type { }; 

iterator_traits<T>::iterator_categoryが悪い形成されている場合は、Tがイテレータではありません。

+0

'iterator_traits 'が 'value_type'、' difference_type'などを(イテレータでネストされた型として提供するか、または 'iterator_traits ')。 [DR 2408](http://wg21.link/lwg2408)以来、 'iterator_traits'が型がイテレータであると思っているかどうかを知るために、式SFINAEの文脈でそれらのどれかを安全に調べることができます。そこで、 'iterator_traits :: iterator_category'を' void_t'引数リストに追加します。 –

+0

@JonathanWakelyだけでなく、他のすべてではなく、その1つを確認してもいいですか? – Barry

+0

はい、おそらくタイプがすべてのネストされた型(または 'iterator_traits'の特殊化)をインクリメント可能でも逆参照不可能でもないと仮定することは安全でしょう。タイプがそれをしたら、あなたはその作者がそれに対処するのではなく、作者を叩いて型を使うことを拒むことができます:) –

1

元のポスターは、イテレータを増分および逆参照できるようにするために、実際にはInputIterator(http://en.cppreference.com/w/cpp/concept/InputIteratorを参照)を識別する方法を求めていることを明らかにしました。これは、標準C++ 11で非常に単純なSFINAEソリューションを持っています。GCCのSTLからのものと同様:

template<typename InputIterator> 
using RequireInputIterator = typename 
    std::enable_if<std::is_convertible<typename 
             std::iterator_traits<InputIterator>::iterator_category, 
             std::input_iterator_tag>::value>::type; 

... 

// Example: declare a vector constructor from a pair of input iterators. 
template <typename InputIterator, typename = RequireInputIterator<InputIterator> > 
    MyVector(InputIterator first, InputIterator last) { /* ... */ }; 

これはアーメンTsirunyanはイテレータ自分自身の必要と思っていたのtypedefを定義するイテレータ型の特性クラスに依存しています。 (反復子がtypedefを提供しますが、イテレータとして裸のポインタを使用するために必要な特性クラスで提供することもできますし、標準のライブラリ実装が必要です)

関連する問題