2016-06-26 3 views
1

Я попытался максимально упростить ситуацию.const iterator зависит от функции begin()

В основном у меня есть класс, который представляет собой динамический Array (здесь показан как class Array с размером 4), а во-вторых класс, который является HashMap, и имеет Array of Arrays в качестве члена (здесь представленного class Bar).

Оба класса реализуют begin() и end(), поэтому вы можете использовать цикл foreach для итерации по всем элементам.

Тип итератора Array - это просто T* и const T* для варианта const. Для Bar есть специальный class Iterator, который правильно выполняет итерации по всем элементам Array<Array<T>>. Теперь вы должны взглянуть на классы, которые я предоставил, чтобы вы знали, о чем я говорю.

Но теперь есть проблема, когда я называю bar.begin() на const Bar объекта


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

Я понял, что проблема заключается в том, что decltype(((T*)nullptr)->begin()) всегда выбирает функцию non-const begin() функции T, что абсолютно имеет смысл, поскольку я не написал (const T*)nullptr.

Если теперь я называю это из константного контекста, она будет не назначать const T* как data.last().end() к внутреннему T* от decltype, который на самом деле должен быть const T*.

я могу обойти эту проблему, объявив второй класс ConstIterator, который делает все так же, как неконстантные один, но использует (const Array<T>*)nullptr и (const T*)nullptr внутри заявления decltype.

Итак, что я могу сделать без копирования всей панели: класс Iterator?


упрощенный код:

template<class T> 
class Array 
{ 
    T data[4]; 

    T last() { return data[3]; } 


    T* begin()    { return data; }; 
    T* end()    { return data + 4; }; 

    const T* begin() const { return data; }; 
    const T* end() const { return data + 4; }; 
} 

template<class T> 
class Bar 
{ 
    class Iterator 
    { 
     using ArrayIterator = decltype(((Array<T>*)nullptr)->begin()); 
     using ElementIterator = decltype(((T*)nullptr)->begin()); 

     Iterator(const ArrayIterator& beg, const ArrayIterator& end) 
     { 
      //initialize the iterator to the first element of the first array 
      //(and rembember end) 
     }; 

     Iterator(const ElementIterator& cur) 
     { 
      //initialize the iterator to the current element 
     }; 

     //++ will iterate go to next element and eventually jump to the next array. 
     //== returns true if the current element is the same 
    }; 

    Array<T> data; 

    Iterator begin()  { return Iterator(data.begin(), data.end()); }; 
    Iterator end()   { return Iterator(data.last().end()); }; 

    Iterator begin() const { return Iterator(data.begin(), data.end()); }; 
    Iterator end() const { return Iterator(data.last().end()); }; 
}; 

ответ

1

На самом деле, вам придется реализовать ConstIterator класс. См., Например, стандартные контейнеры, такие как vector или list, у вас есть как iterator, так и const_iterator, и они разные классы. Корректность иногда бывает сложной. Однако вы можете избежать дублирования кода с помощью шаблонов или const_cast (см., Например, Scott Myers Effective C++)

+2

Не используйте для этого const_cast. –

Смежные вопросы