Я пишу библиотеку, которая содержит множество функций и методов из другой библиотеки. Чтобы избежать копирования возвращаемых значений, я применяю 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
не крадет у t
1-е использование std::forward
в f(std::forward<T>(t))
Это излишне, и это было удалено мной.
Я написал небольшой тест: https://gist.github.com/3910503
Тест показывает, что возвращение неосуществлено T
— создает дополнительную копию в gcc48 и clang32 с -O3 (RVO не запускается).
Кроме того, я не смог получить плохое поведение от UB в:
auto&& tmp = wrapper(42);
Это не доказывает ничего из причины, потому что это не определено поведение (если это UB).
В том случае, если вы знаете, что t
не будет переведен из состояния после вызова f
Два ваших разумных варианта:
вернуть std::forward<T>(t)
с типом T&&
что позволяет избежать любой конструкции, но позволяет писать, например, auto&& ref = wrapper(42);
, который оставляет ref
свисающая ссылка
вернуть std::forward<T>(t)
с типом T
, который в худшем случае запрашивает конструкцию перемещения, когда параметр является значением rvalue — это позволяет избежать вышеуказанной проблемы для значений prvalue, но потенциально может быть украдено из значений xvalue
Во всех случаях вам нужно std::forward
, Копия elision не считается, потому что t
всегда ссылка.
В зависимости от того, что передается этой функции, это приводит к неопределенному поведению! Точнее, если вы передаете не-значение, то есть В качестве значения этой функции значение, на которое ссылается возвращенная ссылка, будет устаревшим.
Также 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
работает.
Нет, вам не нужно использовать std::forward
лучше вообще не возвращать ссылку на r-значение, потому что это может предотвратить оптимизацию NRVO. Вы можете прочитать больше о семантике перемещения в этой статье: Статья