Перегрузка * (итератор + n) и * (n + итератор) в классе итераторов C ++? - PullRequest
0 голосов
/ 10 ноября 2009

(Примечание: я пишу этот проект только для изучения; комментарии о его избыточности ... эээ, избыточны.;)

Я пытаюсь реализовать итератор произвольного доступа, но я нашел очень мало литературы по этому вопросу, поэтому я собираюсь методом проб и ошибок в сочетании со списком прототипов перегрузки операторов в Википедии. Пока это работает достаточно хорошо, но я попал в ловушку.

Код, такой как

exscape::string::iterator i = string_instance.begin();
std::cout << *i << std::endl;

работает и печатает первый символ строки. Однако * (i + 1) не работает, как и * (1 + i). Моя полная реализация, очевидно, была бы слишком большой, но вот суть этого:

namespace exscape {
    class string {
        friend class iterator;
    ...
    public:
        class iterator : public std::iterator<std::random_access_iterator_tag, char> {
            ...
            char &operator*(void) {
                return *p; // After some bounds checking
            }
            char *operator->(void) {
                return p;
            }

            char &operator[](const int offset) {
                return *(p + offset); // After some bounds checking
            }

            iterator &operator+=(const int offset) {
                p += offset;
                return *this;
            }

            const iterator operator+(const int offset) {
                iterator out (*this);
                out += offset;
                return out;
            }

        };
};
}

int main() {
    exscape::string s = "ABCDEF";
    exscape::string::iterator i = s.begin();
    std::cout << *(i + 2) << std::endl;
}

Сказанное выше не работает с (строка 632, конечно, строка * (i + 2)):

string.cpp: в функции int main (): string.cpp: 632: ошибка: нет совпадения для оператора * в * exscape :: string :: iterator :: operator + (int) (2) ’ string.cpp: 105: примечание: кандидаты: char & exscape :: string :: iterator :: operator * ()

* (2 + i) завершается с:

string.cpp: в функции int main (): string.cpp: 632: ошибка: нет совпадения для оператора «+» в «2 + i» string.cpp: 434: примечание: кандидаты: exscape :: string exscape :: operator + (const char *, const exscape :: string &)

Я предполагаю, что мне нужно сделать еще несколько перегрузок, но я не уверен, какой оператор мне не хватает.

Ответы [ 4 ]

9 голосов
/ 10 ноября 2009

Ваш оператор + возвращает константный итератор, но у вас нет константы operator*. Добавьте один, и я думаю, что у вас все будет хорошо. Или, как предлагает xtofl ниже, вы можете сделать operator* const. Это лучший дизайн, если вам не нужен неконстантный operator* по какой-то причине.

4 голосов
/ 10 ноября 2009

Чтобы она работала с числовой константой слева, вам понадобится функция, не являющаяся членом. Примерно так (непроверенный код):

exscape::string::iterator operator+(exscape::string::iterator it, size_t n) {
    return it += n;
}

exscape::string::iterator operator+(size_t n, exscape::string::iterator it) {
    return it += n;
}
3 голосов
/ 10 ноября 2009

Во-первых, вам нужно operator *(void) const.

[Изменить: в зависимости от вашего существующего оператора, следующее должно сделать:

char &operator *(void) const {
    // bounds checking
    return *p;
}

]

Во-вторых, вам нужно operator+(int, exscape::string::iterator). Довольно распространенный способ написать это (в классе итераторов):

friend const iterator operator+(const int offset, iterator out) {
    out += offset;
    return out;
}

Обратите внимание, что пометка этого друга делает его функцией, не являющейся членом, даже если она определена внутри класса. Возможно, вы также захотите заменить operator+(int) функцией, не являющейся членом, operator+(iterator,int), просто чтобы получить те же правила неявного преобразования, которые применяются к LHS и RHS +.

[Другое редактирование: как вы указали в своем комментарии, оператор + не должен возвращать const iterator в любом случае - просто верните iterator. Так что для вашего примера кода вам на самом деле не нужен operator*()const. Но в любом случае он должен быть, потому что пользователи могут захотеть писать код, используя const-модифицированные экземпляры вашего класса.]

Наконец, стандартные контейнеры с итераторами произвольного доступа (включая std :: string) определяют difference_type со знаком в качестве члена класса. int может быть недостаточно большим, чтобы содержать все возможные смещения (например, в архитектуре LP64), но ptrdiff_t является хорошим кандидатом.

0 голосов
/ 10 ноября 2009

Я не верю * (2 + i) будет работать, так как левый операнд должен быть вашего собственного типа. Вы на самом деле говорите компилятору добавить ваш итератор в 2, что не имеет смысла. (i + 2) означает перемещение моего итератора вперед на два индекса.

См. C ++ Faq Lite для получения дополнительной информации.

...