Страницы

Поиск по вопросам

пятница, 5 апреля 2019 г.

Возврат по ссылке/значению примитивных типов

В этом вопросе сказано, что примитивные типы лучше передавать в функцию по значению. Насколько я понимаю, то же верно и для возврата из функции. Тогда почему операторы индексации (например, вектора) возвращают исключительно ссылки, а не специализированны под возврат по значению для фундаментальных типов? Тем более так наверняка будет использована move семантика.
upd то, что я хочу сделать:
#include
template struct A { T x;
T& operator[](int) {return x;} const T& operator[](int) const {return x;} }; template<> struct A { int x;
int& operator[](int) {return x;} int operator[](int) const {return x;} };
int main() { const A a{}; A b{}; //a[0] = 3; b[0] = 3; std::cout << a[0] << ' ' << b[0]; }


Ответ

Если специализировать для всех фундаментальных типов каклй-нибудь стандартный контейнер, который обладает множеством различных методов, то это будет очень не эффективно.
Поэтому оператор индексирования в любом случае возвращает ссылку на объект. Передача по ссылке не является ресурсо-затратной операцией. Объекты при этом не создаются и не копируются. Более того для фундаментальных типов конструктор перемещения, как таковой, не существует. Поэтому и нет никакой необходимости специально перегружать стандартные контейнеры для фундаментальных типов, которых немало.
К тому же получится неоднозначность с определением перегруженного оператора индексации, так как встроенный оператор возвращает всегда lvalue
Есть еще один нюанс. Даже если вы не собираетесь присваивать возвращаемому объекту новое значение, тем не менее у вас может быть необходимость хранить указатели на эти объекты. Например, у вас может возникнуть необходимость создать объект типа std::reference_wrapper допустим с помощью функции std::cref, который будет указывать на исходный объект в контейнере. Если не передавать ссылку на объект из контейнера, то вы не сможете воспользоваться такой возможностью.
Рассмотрите следующий пример
#include #include #include
int main() { const std::vector v = { 3, 2, 4, 5, 1 };
std::vector> rv = { std::cref(v[0]), std::cref(v[1]), std::cref(v[2]), std::cref(v[3]), std::cref(v[4]) };
std::sort(rv.begin(), rv.end(), [](auto &a, auto &b) { return a.get() < b.get(); });
for (int x : v) std::cout << x << ' '; std::cout << std::endl; for (const auto &r : rv) std::cout << r.get() << ' '; std::cout << std::endl; }
Вывод программы на консоль
3 2 4 5 1 1 2 3 4 5
В этой программе объявлен константный вектор. Однако, так как оператор operator [] возвращает ссылку на исходный объект, то можно связать его с объектом std::reference_wrapper, и тем самым представлять элементы исходного вектора упорядочными по различным критериям.
То есть неважно, является ли объект константным или нет, тем не менее нередко необходимо получить на него ссылку, чтобы отслеживать его состояние. Константные объекты могут помимо прочего быть еще и volatile объектами или иметь члены данных с модификатором mutable
Если же возвращать объект по значению из оператора, то связь с исходным объектом будет утеряна, и вы будете иметь дело с копией.

Комментариев нет:

Отправить комментарий