Подтвердить что ты не робот

Перемещать или не переходить из метода ref-qual r-value?

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

#include <utility>


struct Bar
{
};

struct Foo
{
    Bar bar;

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

Ответ 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, реализующий такой оператор конкатенации, может уменьшить количество перераспределения, по существу выполняющие как выделенный построитель строк.