Переместить или не перейти от r-значения ref-квалифицированного метода?

В следующем C ++ 11 + коде, какой оператор возврата должен быть предпочтительным?

#include <utility>struct Bar
{
};

struct Foo
{
Bar bar;

Bar get() &&
{
return std::move(bar); // 1
return bar;            // 2
}
};

13

Решение

Ну, так как это R-значение Ref квалифицированная функция-член, this предположительно истекает. Так что имеет смысл двигаться bar вне, предполагая Bar на самом деле получает что-то от перемещения.

поскольку bar является членом, а не параметром локального объекта / функции, обычные критерии для исключения копирования в операторе возврата не применяются. Это всегда будет копировать, если вы явно std::move Это.

Поэтому мой ответ — перейти к варианту номер один.

19

Другие решения

Я предпочитаю вариант 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); }

Мы ценимся, поэтому мы должны быть свободны в каннибализации наших ресурсов, поэтому move-ную bar верно. Но только потому, что мы открыты для переезда bar не означает, что мы должны назначить такой шаг и выполнить дополнительные операции, поэтому мы должны просто вернуть ссылку на него.

Так поступает стандартная библиотека — например, std::optional<T>::value.

4

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

1
По вопросам рекламы [email protected]