Здесь вызов std::move следует убрать. Несмотря на то, что в коде пытаются "подсказать" компилятору, что возвращаемый объект следует перемещать, а не копировать, используя функцию std::move, компилятор будет обязан сгенерировать более медленный ассемблерный код.
Дело в том, что возвращаемый объект – это результат вызова std::move, его тип будет Res &&. Тип фактически возвращаемого объекта и тип возвращаемого объекта по сигнатуре функции различны. Следовательно, компилятор не сможет применить для функции foo NRVO, и мы имеем дело не с оптимизацией, а с пессимизацией.
Более того, согласно стандарту C++11, если компилятор не сможет применить необязательную оптимизацию, то он должен сначала применить конструктор перемещения и лишь затем конструктор копирования для локальных переменных или формальных параметров функции.
Честно сказать, не уверен что это к данному случаю. Но как-то все слишком завязано на эти хитрые трюки компилятора по оптимизации, а хотелось бы простоты в данном случае. Даже не полагаться на RVO (вдруг где-то не сработает) это — забрать владение чисто и прозрачно.
Здравствуйте, Shmj, Вы писали:
S>Дело в том, что возвращаемый объект – это результат вызова std::move, его тип будет Res &&. Тип фактически возвращаемого объекта и тип возвращаемого объекта по сигнатуре функции различны. Следовательно, компилятор не сможет применить для функции foo NRVO, и мы имеем дело не с оптимизацией, а с пессимизацией.
В данном случае NRVO невозможно, поэтому мув нужен
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
S>- будет создавать полную копию всех данных. Как бы постоянная ловушка. Т.е. нужно не забывать писать:
S>
S>std::vector<uint8_t>& t = obj.getData();
S>
Непонятно причем тут вектор?
Оно будет работать аналогично с любым типом возвращаемого объекта.
Хочешь ссылку — дай знать об этом компилятору.
S>И далее. Что если я захочу в итоге переместить данные, забрать владение у MyClass — как красивее оформить?
S>
S>Здесь вызов std::move следует убрать. Несмотря на то, что в коде пытаются "подсказать" компилятору, что возвращаемый объект следует перемещать, а не копировать, используя функцию std::move, компилятор будет обязан сгенерировать более медленный ассемблерный код.
Здравствуйте, Shmj, Вы писали:
S>А что там будет — просто RVO? Это гарантируется на всех компиляторах?
data — это же член класса? Тут RVO не сработает. Функция, которая возвращает локальный объект через RVO на самом деле сразу аллоцирует его в стеке вызывающей функции.
Тут же член класса, никакого RVO не будет.
— не создается копия данных на каждый чих, что более разумно на мой взгляд.
M>Оно будет работать аналогично с любым типом возвращаемого объекта. M>Хочешь ссылку — дай знать об этом компилятору.
M>А если вернуть результат как &&?
Здравствуйте, tapatoon, Вы писали:
T>data — это же член класса? Тут RVO не сработает. Функция, которая возвращает локальный объект через RVO на самом деле сразу аллоцирует его в стеке вызывающей функции. T>Тут же член класса, никакого RVO не будет.
Здравствуйте, Shmj, Вы писали:
S>- не создается копия данных на каждый чих, что более разумно на мой взгляд.
Нет.
Просто есть два подхода:
1. В одном при создании копии объекта данные не копируются и вводится явная функция типа clone. В OpenCV, например, такой подход.
2. В другом при создании копии данные копируются, но ты можешь пользоваться ссылками. В STL такой подход.
Определяется это типичными задачами в которых твой объект юзаются.
Здравствуйте, Shmj, Вы писали:
TB>>Тогда будет RVO S>Гарантируется ли он на всех компиляторах под все платформы или же не факт? Можно ли на это опираться?
Да нет никакого RVO, просто вернется ссылка. Вот если бы был тип возвращаемого значения вектор без ссылки, тогда да.
Здравствуйте, andrey.desman, Вы писали:
TB>>>Тогда будет RVO S>>Гарантируется ли он на всех компиляторах под все платформы или же не факт? Можно ли на это опираться? AD>Да нет никакого RVO, просто вернется ссылка. Вот если бы был тип возвращаемого значения вектор без ссылки, тогда да.
Да и то, тут возможна копия, потому как промежуточная ссылка, но не уверен — тут недавно было обсуждение на эту тему.
S>>std::vector<uint8_t>&& takeData()
S>>{
S>> std::vector<uint8_t>&& temp = std::move(data); // temp - rvalue-ссылка на data
S>> return temp;
S>>}
S>>
S>>А вот так если записать?
TB>Тогда будет RVO TB>Но этот код эквивалентен
TB>
TB>return std::move(data)
TB>
Не будет здесь никакого RVO. Во втором варианте будет просто возврат rvalue ссылки и только. Причём, будет ли выполнено перемщение данных вектора, не известно — это зависит от того, каким образом вызывающая сторона использует полученную ссылку. Вариант же, предложенный Shmj и вовсе не скомпилируется, поскольку выражение temp относится к категории lvalue и не может быть связано с rvalue сылкой.
А чтобы был RVO (aka guaranteed copy elision), нужно, чтоб выражение под return относилось к категории prvalue и тип этого выражения должен совпадать с типом возращаемого значения функции. Выражение же std::move(data) принадлежит к категори xvalue.
--
Справедливость выше закона. А человечность выше справедливости.
S>- не создается копия данных на каждый чих, что более разумно на мой взгляд.
У меня такое ощущение, что ты пытаешься писать на C++ как на C#. По-моему, давно пора бы расстаться с иллюзиями и понять, что это разные языки. Если ты не хочешь, чтоб создавалась копия, значит, нужно объявлять ссылку. Мало того, нужно ещё и подобрать правильный тип для ссылки — lvalue или rvalue, константная или неконстантная.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Не будет здесь никакого RVO. Во втором варианте будет просто возврат rvalue ссылки и только. Причём, будет ли выполнено перемщение данных вектора, не известно — это зависит от того, каким образом вызывающая сторона использует полученную ссылку. Вариант же, предложенный Shmj и вовсе не скомпилируется, поскольку выражение temp относится к категории lvalue и не может быть связано с rvalue сылкой.
Здравствуйте, T4r4sB, Вы писали:
TB>Я имею в виду такой вариант: https://godbolt.org/z/5q55ofhaj TB>а вот ставить && при объявлении переменных это шиза конечно
Ну так и в этом случае не RVO, а перемещение. Что и видно по выхлопу.
#include <vector>
#include <cstdint>
#include <cstring>
#include <cstdio>
struct T {
int value{};
explicit T(int value) : value(value) { printf("T(%d)\n", value);}
~T() { printf("~T(%d)\n", value);}
T(const T&) = delete;
T(T&&) = delete;
T& operator=(const T&) = delete;
T& operator=(T&&) = delete;
};
T takeData(int value)
{
return T(value);
}
int main() {
auto t = takeData(42);
}
С практической точки зрения гарантированное RVO, которое ввели, начиная с C++17, позволяет определять кастомные конструкторы за пределами класса. С определёнными ограничениями, конечно же.
--
Справедливость выше закона. А человечность выше справедливости.