7

std::vector::iteratorコンパイル時など、特定の型が既知のネストされた型のインスタンスであるかどうかを検出する必要があるという問題があります。タイプ特性を作成したいis_std_vector_iterator

#include <type_traits>
#include <vector>

template<typename T> struct is_std_vector_iterator : std::false_type {};

template<typename T, typename Allocator>
  struct is_std_vector_iterator<typename std::vector<T,Allocator>::iterator>
    : std::true_type
{};

int main()
{
  return 0;
}

しかし、コンパイラエラーが発生します。

$ g++ -std=c++0x test.cpp 
test.cpp:7: error: template parameters not used in partial specialization:
test.cpp:7: error:         ‘T’
test.cpp:7: error:         ‘Allocator’

のような依存型をチェックすることは可能std::vector<T,Allocator>::iteratorですか?


このような特性のやる気を起こさせるユースケースは次のとおりです。

template<typename Iterator>
Iterator my_copy(Iterator first, Iterator last, Iterator result, std::true_type)
{
  // iterators are just pointer wrappers; call memcpy
  memcpy(&*result, &*first, sizeof(typename Iterator::value_type) * last - first);
  return result + last - first;
}

template<typename Iterator>
Iterator my_copy(Iterator first, Iterator last, Iterator result, std::false_type)
{
  // use a general copy
  return std::copy(first, last, result);
}

template<typename Iterator>
Iterator my_copy(Iterator first, Iterator last, Iterator result)
{
  // dispatch based on the type of Iterator
  return my_copy(first, last, result, typename is_std_vector_iterator<Iterator1>::type())
}
4

3 に答える 3

3

さて、最も単純なケースのシナリオでは、次のようになります。

#include <type_traits>
#include <vector>
#include <list>
#include <cstdio>

template <typename T>
typename std::enable_if<
    std::is_same<typename std::vector<typename T::value_type>::iterator, T>::value
    , void>::type
do_something (T begin, T end)
{
    std::printf ("Got vector iterators!\n");
}

template <typename T>
typename std::enable_if<
    !std::is_same<typename std::vector<typename T::value_type>::iterator, T>::value
    , void>::type
do_something (T begin, T end)
{
    std::printf ("Got something other than vector iterators!\n");
}

template <typename T>
typename std::enable_if<std::is_pod<T>::value, void>::type
do_something (T begin, T end)
{
    std::printf ("Got some POD iterators!\n");
}

int main()
{
    std::vector<int> ivec;
    std::list<int> ilist;
    char cdata[64];

    do_something (ivec.begin (), ivec.end ());
    do_something (ilist.begin (), ilist.end ());
    do_something (&cdata[0], cdata + 32);

    return 0;
}

しかし、本当の問題は、誰かがデフォルトのものとは異なるアロケータを使用することを決定したときに起こります。よく知られているテンプレートではなく、よく知られているタイプに対してイテレータをチェックしたいので、基本的にこれを使用して、認識しているいくつかのアロケータで拡張することができます。そうでなければ、異なるタイプでインスタンス化されたテンプレートは異なるタイプであり、タイプが任意のパラメーターに特化したテンプレートのインスタンスであるかどうかをテストする方法があるかどうかはわかりません。おそらくそのような方法はありません。

一方、この問題の解決方法は異なる場合があります。たとえば、これがstd::vector<...>イテレータであるかどうかによって、どのような違いが生じますか?ランダムアクセスかどうかなどを確認するのが理にかなっているかもしれません。

アップデート:

継続的にレイアウトされたメモリの場合、最善の策は、イテレータ特性を使用して、ランダムアクセスタグをチェックすることです。例えば:

#include <type_traits>
#include <functional>
#include <vector>
#include <list>
#include <cstdio>

template <typename T>
struct is_random_access_iterator : std::is_same<
    typename std::iterator_traits<T>::iterator_category
    , std::random_access_iterator_tag>
{};

template <typename T>
typename std::enable_if<is_random_access_iterator<T>::value>::type
do_something (T begin, T end)
{
    std::printf ("Random access granted!\n");
}

template <typename T>
typename std::enable_if<!is_random_access_iterator<T>::value>::type
do_something (T begin, T end)
{
    std::printf ("No random access for us today!\n");
}

int main()
{
    std::vector<int> ivec;
    std::list<int> ilist;
    char cdata[32];

    do_something (ivec.begin (), ivec.end ());
    do_something (ilist.begin (), ilist.end ());
    do_something (&cdata[0], cdata + sizeof (cdata) / sizeof (cdata[0]));

    return 0;
}

std::vectorこれは、アロケータでチェックするよりも間違いなく単純で、さらに堅実です。ただし、この場合でも、誰かが本当に必要な場合は、メモリのさまざまなチャンクへのシームレスなアクセスを提供するランダムアクセスイテレータを提供して購入することができます。これをポインタに変換すると、イテレータではなくポインタ演算を使用して大きな問題が発生します。オーバーロードされた演算子。生のポインタとイテレータの両方を変更しながらメモリアドレスを比較するだけで、それから身を守ることができますが、ジュースはありません。

それが役に立てば幸い。

于 2011-11-12T00:57:54.067 に答える
1

きれいなプリンターis_container_helperからtypetraitを見る必要があります。そのライブラリのより洗練されたパブリックバージョンでは、typetraitと呼びます(例:ここ):has_const_iterator

template<typename T>
struct has_const_iterator
{
private:
    typedef char                      yes;
    typedef struct { char array[2]; } no;

    template<typename C> static yes test(typename C::const_iterator*);
    template<typename C> static no  test(...);
public:
    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
    typedef T type;
};
于 2011-11-12T00:38:13.283 に答える
1

AFAIK、イテレータの値型はから取得できますiterator_traits<Iter>::value_type。次に、それstd::vector<that_value_type, Alloc>::iteratorが本当にそれであるかどうかを確認できます(例:with boost::is_same

やる気を起こさせる例から、推測するのにおそらく問題があることがわかります。Allocカスタムアロケータを使用する予定がない場合は、デフォルトのままにしておきます。すべてで機能する一般的な解決策はありませんAlloc

于 2011-11-12T00:49:30.050 に答える