Страницы

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

воскресенье, 2 февраля 2020 г.

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

#cpp #ссылки #перегрузка_операторов


В этом вопросе сказано, что примитивные типы лучше передавать в функцию по значению.
Насколько я понимаю, то же верно и для возврата из функции. Тогда почему операторы
индексации (например, вектора) возвращают исключительно ссылки, а не специализированны
под возврат по значению для фундаментальных типов? Тем более так наверняка будет использована
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];
}

    


Ответы

Ответ 1



Если специализировать для всех фундаментальных типов каклй-нибудь стандартный контейнер, который обладает множеством различных методов, то это будет очень не эффективно. Поэтому оператор индексирования в любом случае возвращает ссылку на объект. Передача по ссылке не является ресурсо-затратной операцией. Объекты при этом не создаются и не копируются. Более того для фундаментальных типов конструктор перемещения, как таковой, не существует. Поэтому и нет никакой необходимости специально перегружать стандартные контейнеры для фундаментальных типов, которых немало. К тому же получится неоднозначность с определением перегруженного оператора индексации, так как встроенный оператор возвращает всегда 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. Если же возвращать объект по значению из оператора, то связь с исходным объектом будет утеряна, и вы будете иметь дело с копией.

Ответ 2



Думаю, это то, что требуется. template struct A { using ConstIndexRetType = typename std::conditional::value, T, const T&>::type; T x; T& operator[](int) { return x; } ConstIndexRetType operator[](int) const { return x; } }; int main(int argc, char **argv) { struct C {}; const A a; const A b; static_assert(std::is_same::value, "?"); static_assert(std::is_same::value, "?"); }

Ответ 3



А давайте глянем на практике... Так сказать, найдите три отличия: https://godbolt.org/g/s95gJt Вот это void f(const A& a, A& b) { std::cout << a[0]; std::cout << b[0]; } компилируется в push rbx mov rbx, rsi mov esi, DWORD PTR [rdi] mov edi, OFFSET FLAT:std::cout call std::basic_ostream >::operator<<(int) mov esi, DWORD PTR [rbx] mov edi, OFFSET FLAT:std::cout pop rbx jmp std::basic_ostream >::operator<<(int) Как видите, разницы никакой.

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

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