Перемещать или не переходить из метода ref-qual r-value?
В следующем коде С++ 11 +, который должен быть предпочтительным построением выражений?
#include <utility>
struct Bar
{
};
struct Foo
{
Bar bar;
Bar get() &&
{
return std::move(bar); // 1
return bar; // 2
}
};
Ответы
Ответ 1
Ну, так как это r-значение ref квалифицированной функции-члена, this
, вероятно, истекает. Поэтому имеет смысл перемещать bar
out, предполагая, что bar
фактически получает что-то от перемещения.
Так как bar
является членом, а не локальным параметром object/function, обычные критерии для копирования elision в операторе return не применяются. Он всегда копируется, если вы явно не std::move
его.
Итак, мой ответ - перейти к опции номер один.
Ответ 2
Я предпочитаю вариант 3:
Bar&& get() &&
// ^^
{
return std::move(bar);
}
и, пока мы на нем:
Bar& get() & { return bar; }
Bar const& get() const& { return bar; }
Bar const&& get() const&& { return std::move(bar); }
Мы rvalue, поэтому он должен быть свободен для cannibilize наших ресурсов, поэтому move
-ing bar
является правильным. Но только потому, что мы открыты для перемещения bar
, это не значит, что мы должны прописать такой шаг и выполнить дополнительные операции, поэтому нам нужно просто вернуть ссылку rvalue на него.
Вот как это делает стандартная библиотека. std::optional<T>::value
.
Ответ 3
Я хотел бы уточнить свою точку зрения (из комментариев). Несмотря на то, что движущийся результат должен быть значительно более эффективным, чем копирование, это не моя главная проблема. Основная проблема связана с ложным предположением о том, что, вызвав этот метод на ссылку r-value на Foo
, намерение вызывающего абонента экземпляра включает в себя создание нового значения Bar
. Например:
Foo Produce_Foo(void);
// Alright, caller wanted to make a new `Bar` value, and by using `move`
// we've avoided a heavy copy operation.
auto bar{Produce_Foo().get()};
// Oops! No one asked us to make a useless temporary...
cout << Produce_Foo().get().value() << endl;
Решение состоит в том, чтобы добавить специальные функции, которые будут использоваться, чтобы заглянуть в сохраненную панель и взять контроль над содержимым сохраненного объекта панели.
Bar const & get_bar() const noexcept
{
return bar;
}
// no particular need to this method to be r-value reference qualified
// because there is no direct correlation between Foo instance being moved / temp
// and intention to take control over content of stored bar object.
Bar give_bar() noexcept
{
return ::std::move(bar);
}
Теперь у пользователя есть выбор, проблем больше не будет:
// Alright, caller wanted to make a new `Bar` value, and by using `move`
// we've avoided a heavy copy operation.
// There is also no need to figure out whether Produce_Foo returned an rvalue or not.
auto bar{Produce_Foo().give_bar()};
// Alright, no extra temporaries.
cout << Produce_Foo().get_bar().value() << endl;
Что касается примеров использования для квалифицированных методов ссылки на r-значение, я думаю, что они в основном полезны при работе с временными рядами того же типа, что и этот объект. например класс string, реализующий такой оператор конкатенации, может уменьшить количество
перераспределения, по существу выполняющие как выделенный построитель строк.