C++ Ошибка с оператором * в пользовательском итераторе для b-дерева

Я получаю следующую ошибку при попытке вернуть значение с помощью оператора *() в моем пользовательском итераторе.

 error: invalid initialization of non-const reference of type 'char&' from a temporary of type 'char'

Если кто-то может мне помочь, это было бы здорово!

Особенности кода заключаются в следующем:

Тестовый код:

  for(btree<char>::iterator itr = tree.begin(); itr != tree.end(); ++itr) {

    cout << *itr << " "; // <- this line complains about the * operator.
  }

Код оператора*():

template <typename T> typename btree_iterator<T>::reference btree_iterator<T>::operator*() const {

  return pointee_->value(); // <- this line is where the error above is produced from.
}

pointee_ из класса btree_iterator — это закрытая переменная, определяемая следующим образом:

btree_node<T>* pointee_;

ссылка в классе btree_iterator имеет следующий тип:

  typedef T&       reference;

value() определено в классе btree_node следующим образом:

T value() const;

// function definition.
template <typename T> T btree_node<T>::value() const { return value_; }

значение изначально сохраняется в btree_node через его конструктор следующим образом:

template <typename T> btree_node<T>::btree_node(const T& elem) : value_(elem), nextCont_(NULL), prevCont_(NULL), nextNode_(NULL), prevNode_(NULL) {}

Пожалуйста, дайте мне знать, если я пропустил какую-либо важную информацию, и извините, если я это сделал!

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

Заранее спасибо и с уважением!


person Mick    schedule 17.10.2011    source источник
comment
Вместо разделения всех функций было бы неплохо, если бы они были в одном блоке, готовом для копирования и вставки в компилятор - см. это для получения дополнительной информации.   -  person Ayjay    schedule 17.10.2011
comment
Вместо этого попробуйте вернуть постоянную ссылку.   -  person Paul Manta    schedule 17.10.2011


Ответы (2)


Вы пытаетесь использовать ссылку на временную переменную

template <typename T> T btree_node<T>::value() const { return value_; }
                     ^^^

Это возвращает временную копию value_.

template <typename T> typename btree_iterator<T>::reference btree_iterator<T>::operator*() const {
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  return pointee_->value(); // <- this line is where the error above is produced from.
}

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

person Ayjay    schedule 17.10.2011
comment
Спасибо, Айджай, похоже, это моя проблема! Я отмечу проблему решенной, как только разберусь с ней в голове :) - person Mick; 17.10.2011

pointee_->value() возвращает значения, которые в основном представляют собой новую копию. Следовательно, это значение является локальным в operator*, и вы не можете вернуть ссылку на него.

Я бы рекомендовал делать это так же, как и большинство контейнеров, то есть иметь оба, iterator и const_iterator, тогда как const_iterator возвращает ссылку на константу, а итератор возвращает ссылку. Тот же принцип применим к value(). Имейте версии, возвращающие ref и const ref.

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

Итераторы не делают копии где-то, а итерируют данные внутри структуры данных. Если вам нужны копии, вы можете их сделать.

person b.buchhold    schedule 17.10.2011
comment
Спасибо b.buchhold за дальнейшее разъяснение! Небольшой вопрос: когда вы говорите, что нужно иметь 2 версии value(), вы имеете в виду перегрузку одной, чтобы иметь тип возвращаемого значения const T&, и одну с типом возвращаемого значения только T&? Похоже, компиляция не позволяет мне перегружаться таким образом. Кроме того, да, у меня будет константный и неконстантный итератор :) - person Mick; 17.10.2011
comment
Вы также сделаете одну из них константной функцией-членом, т.е. const T& value() const; против T& value(); что должно быть допустимой перегрузкой - person b.buchhold; 17.10.2011