Почему оператор не перегружен для lvalues и rvalues?
Стандартные контейнеры С++ предлагают только одну версию operator[]
для контейнеров, таких как vector<T>
и deque<T>
. Он возвращает T&
(кроме vector<bool>
, который я проигнорирую), что является lvalue. Это означает, что в коде, подобном этому,
vector<BigObject> makeVector(); // factory function
auto copyOfObject = makeVector()[0]; // copy BigObject
copyOfObject
будет построена копия. Учитывая, что makeVector()
возвращает rvalue vector
, представляется разумным ожидать, что конструкция copyOfObject
будет построена.
Если operator[]
для таких контейнеров был перегружен для объектов rvalue и lvalue, то operator[]
для контейнеров rvalue мог возвращать ссылку rvalue, то есть rvalue:
template<typename T>
container {
public:
T& operator[](int index) &; // for lvalue objects
T&& operator[](int index) &&; // for rvalue objects
...
};
В этом случае copyOfObject
будет построено движение.
Есть ли причина, по которой такая перегрузка будет плохой идеей вообще? Есть ли причина, почему это не было сделано для стандартных контейнеров в С++ 14?
Ответы
Ответ 1
Преобразование комментария в ответ:
В этом подходе нет ничего неправильного; доступ к члену класса соответствует аналогичному правилу (E1.E2
является значением xvalue, если E1
является rvalue, а E2
обозначает нестатический элемент данных и не является ссылкой, см. [expr.ref]/4.2) и элементами внутри контейнера логически похожи на нестатические элементы данных.
Значительная проблема с выполнением этого для std::vector
или других стандартных контейнеров заключается в том, что, скорее всего, он сломает некоторый старый код. Рассмотрим:
void foo(int &);
std::vector<int> bar();
foo(bar()[0]);
Эта последняя строка прекратит компиляцию, если operator[]
в векторе rvalue возвращает значение x. В качестве альтернативы - и, возможно, хуже - если есть перегрузка foo(const int &)
, она беззвучно начнет вызывать эту функцию.
Также, возвращая кучу элементов в контейнере и используя только один элемент, уже довольно неэффективно. Это доказывает, что код, который делает это, вероятно, все равно не заботится о скорости, и поэтому небольшое улучшение производительности не стоит вводить потенциально разрушающие изменения.
Ответ 2
Я думаю, что вы оставите контейнер в недопустимом состоянии, если вы выйдете из одного из элементов, я бы сказал, что нужно разрешить это состояние вообще. Во-вторых, если вам это нужно, не можете ли вы просто вызвать конструктор перемещения нового объекта следующим образом:
T copyObj = std::move(makeVector()[0]);
Update:
Наиболее важным моментом является, опять же, на мой взгляд, что контейнеры представляют собой контейнеры по своей природе, поэтому они не должны каким-либо образом изменять элементы внутри них. Они просто предоставляют хранилище, механизм итерации и т.д.