Пересылка возвращаемых значений. Нужен ли std :: forward?

Я пишу библиотеку, которая содержит множество функций и методов из другой библиотеки. Чтобы избежать копирования возвращаемых значений, я применяю std::forward вот так:

template<class T>
T&& wrapper(T&& t) {
f(t);  // t passed as lvalue
return std::forward<T>(t);
}

f возвращается void и берет T&& (или перегружен на ценность). Wrapper всегда возвращает параметр обертки и при возвращаемом значении должен сохранять значение аргумента. Мне действительно нужно использовать std::forward в return? RVO делает это лишним? Делает ли тот факт, что это ссылка (R или L), лишним? Нужно ли, если return не является последним оператором функции (внутри некоторых if)?

Это спорно, если wrapper() должен вернуться void или же T&&потому что вызывающая сторона имеет доступ к оцененному значению через arg (который является ссылкой, R или L). Но в моем случае мне нужно вернуть значение так, чтобы wrapper() можно использовать в выражениях.

Возможно, это не имеет отношения к вопросу, но известно, что функции f не крадет у t1-е использование std::forward в f(std::forward<T>(t)) Это излишне, и это было удалено мной.

Я написал небольшой тест: https://gist.github.com/3910503

Тест показывает, что возвращение неосуществлено T— создает дополнительную копию в gcc48 и clang32 с -O3 (RVO не запускается).

Кроме того, я не смог получить плохое поведение от UB в:

auto&& tmp = wrapper(42);

Это не доказывает ничего из причины, потому что это не определено поведение (если это UB).

30

Решение

В том случае, если вы знаете, что t не будет переведен из состояния после вызова fДва ваших разумных варианта:

  • вернуть std::forward<T>(t) с типом T&&что позволяет избежать любой конструкции, но позволяет писать, например, auto&& ref = wrapper(42);, который оставляет ref свисающая ссылка

  • вернуть std::forward<T>(t) с типом T, который в худшем случае запрашивает конструкцию перемещения, когда параметр является значением rvalue — это позволяет избежать вышеуказанной проблемы для значений prvalue, но потенциально может быть украдено из значений xvalue

Во всех случаях вам нужно std::forward, Копия elision не считается, потому что t всегда ссылка.

9

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

В зависимости от того, что передается этой функции, это приводит к неопределенному поведению! Точнее, если вы передаете не-значение, то есть В качестве значения этой функции значение, на которое ссылается возвращенная ссылка, будет устаревшим.

Также T&& не является «универсальной ссылкой», хотя эффект в некотором роде T можно вывести как T& или же T const&, Проблемный случай, когда это выводится как T: аргументы передаются как временные и умирают после того, как функция вернется, но прежде чем что-либо получит ссылку на нее.

Использование std::forward<T>(x) ну, в общем, ограничивается пересылкой объектов при вызове другой функции: то, что во временном порядке выглядит внутри lvalue, выглядит как lvalue. С помощью std::forward<T>(x) Давайте x выглядеть как временное, если оно вошло как единое целое, и, таким образом, позволяет двигаться от x при создании аргумента вызываемой функции.

Когда вы возвращаете объект из функции, есть несколько сценариев, о которых вы могли бы позаботиться, но ни один из них не включает std::forward():

  • Если тип на самом деле является ссылкой, либо const или неconstВы не хотите ничего делать с объектом и просто возвращаете ссылку.
  • Я упал return операторы используют одну и ту же переменную, или все используют временную, можно использовать elision для копирования / перемещения, которая будет использоваться на приличных компиляторах. Поскольку копирование / перемещение — это оптимизация, это не обязательно происходит, однако.
  • Если всегда возвращается одна и та же локальная переменная или временная переменная, ее можно удалить, если есть конструктор перемещения, в противном случае объект будет скопирован.
  • Когда возвращаются разные переменные или когда возвращение включает в себя выражение, ссылки все равно могут быть возвращены, но исключение копирования / перемещения не будет работать, а также не будет возможности напрямую перейти от результата, если он не является временным. В этих случаях вам нужно использовать std::move() разрешить перемещение с локального объекта.

В большинстве этих случаев производимый тип T и ты должен вернуться T скорее, чем T&&, Если T если это тип lvalue, результат может не быть типом lvalue, однако может потребоваться удалить ссылочную квалификацию из возвращаемого типа. В сценарии вы специально спросили о типе T работает.

6

Нет, вам не нужно использовать std::forward лучше вообще не возвращать ссылку на r-значение, потому что это может предотвратить оптимизацию NRVO. Вы можете прочитать больше о семантике перемещения в этой статье: Статья

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