2012-03-21 2 views
2

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

#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.

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

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?

+0

'& * test2.begin()' выглядит некрасиво! – iammilind

+0

Его уродливый, но он работает ... – dsharlet

+1

Этого не может быть, я боюсь. Единственная возможность - специализироваться на конкретные итераторы, такие как 'vector :: iterator'. Обратите внимание, что 'vector :: reverse_iterator' небезопасен. –

ответ

1

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


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

+0

Спасибо за ваш ответ. См. Мои изменения, я пытаюсь реализовать то, что вы описываете, но я не понимаю, как определить специализацию для конкретных классов итераторов. – dsharlet

+0

Я пробовал, и я потерпел неудачу. Полагая, что нет разумного и стандартного решения. Но я попробую еще кое-что, и если найду что-нибудь, я отправлю его здесь. –