Специализирующийся класс для итераторов, конвертируемых в указатели - PullRequest
2 голосов
/ 21 марта 2012

Я пытаюсь разработать класс, который позволил бы мне эффективно обращаться к контейнеру / указателю через семантику итератора, когда это можно сделать правильно, и когда итераторы не могут быть преобразованы в указатели. Я хочу скопировать диапазон итератора во временный буфера и вернуть этот указатель вместо этого. Для этого я написал следующую программу:

#include <cassert>
#include <vector>
#include <deque>
#include <list>

// General case copies data to temporary vector, in case iterators are from a list or otherwise.
template < typename Iterator, typename tag = std::iterator_traits < Iterator >::iterator_category >
class IteratorBuffer
{
    typedef typename std::iterator_traits < Iterator >::value_type T;

    std::vector < T > temp;
public:
    IteratorBuffer(Iterator begin, Iterator end) : temp(std::distance(begin, end)) 
    { 
        std::copy(begin, end, temp.begin()); 
    } 

    const T * data() { return temp.data(); }
};

// Special case should be invoked if Iterator can safely be treated as a pointer to the range.
template < typename Iterator >
class IteratorBuffer < Iterator, std::random_access_iterator_tag >
{
    typedef typename std::iterator_traits < Iterator >::value_type T;

    const T * temp;
public:
    IteratorBuffer(Iterator begin, Iterator end) : temp(&*begin) { }

    const T * data() { return temp; }
};

int main(int argc, char ** argv)
{
    std::vector < int > test1(10);
    IteratorBuffer < std::vector < int >::iterator > temp1(test1.begin(), test1.end());
    // This should be pointing to the data in test1.
    assert(temp1.data() == test1.data());

    std::list < int > test2;
    for(int i = 0; i < 10; ++i)
        test2.push_back(i);
    IteratorBuffer < std::list < int >::iterator > temp2(test2.begin(), test2.end());
    // This must not point to the beginning iterator.
    assert(temp2.data() != &*test2.begin());

    int test3[10];
    IteratorBuffer < int * > temp3(&test3[0], &test3[10]);
    // This should point to the array.
    assert(temp3.data() == &test3[0]);

    std::deque < int > test4;
    for(int i = 0; i < 10; ++i)
        test4.push_back(i);
    IteratorBuffer < std::deque < int >::iterator > temp4(test4.begin(), test4.end());
    // This must not point to the beginning iterator, not safe.
    assert(temp4.data() != &*test4.begin());
}

Это не проходит последний тест, потому что итераторы std :: deque имеют random_access_iterator_tag.

Как мне написать этот класс так, чтобы он вообще работал правильно?

Полагаю, я должен упомянуть, что использую VC ++ 2010.

Редактировать: Как говорит Адам (я боялся этого), это невозможно напрямую. Теперь я пытаюсь определить свои собственные черты, которые позволяют мне это сделать. Смотрите мою попытку ниже:

template < typename Iterator >
struct IteratorTraits 
{ 
    enum { IsPointerCompatible = false }; 
    typedef typename std::iterator_traits < Iterator >::value_type T;
};
template < typename T >
struct IteratorTraits < T * > 
{ 
    enum { IsPointerCompatible = true }; 
    typedef T T;
};
template < typename T >
struct IteratorTraits < const T * > 
{ 
    enum { IsPointerCompatible = true }; 
    typedef const T T;
};
//template < typename T >
//struct IteratorTraits < typename std::vector < T >::iterator > 
//{ 
//  enum { IsPointerCompatible = true };
//  typedef T T;
//};
//template < typename T, size_t N >
//struct IteratorTraits < typename std::array < T, N >::iterator > 
//{ 
//  enum { IsPointerCompatible = true };
//  typedef T T;
//};

Я опустил классы IteratorBuffer, потому что они очень похожи на классы, использующие std :: iterator_traits.

Первые две специализации работают, но две прокомментированные структуры черт не будут работать. Как мне написать их так, чтобы они работали, не будучи зависимыми от моей конкретной реализации STL?

1 Ответ

1 голос
/ 21 марта 2012

Вы можете специализироваться для указателей в явном виде, а затем для любых контейнеров (о которых вы знаете), какие итераторы могут обрабатываться таким образом (как в случае с std::vector). Это не так уж и плохо, так как нет общей «черты» контейнера, который сказал бы, что его итераторы могут использоваться в качестве указателей. Это гарантия, которую провайдер контейнера должен предоставить явно.


Обратите также внимание, что общая версия IteratorBuffer предполагает наличие по крайней мере категории итераторов прямого преобразования и завершится ошибкой с итераторами только для ввода (поскольку он использует диапазон дважды).

...