Ответ 1
Небольшой пример показывает намерение автора:
#include <tuple>
#include <iostream>
struct A {
A() { }
A(const A&) { std::cout << "copy\n"; }
A(A&&) { std::cout << "move\n"; }
};
template <typename Arg>
static std::tuple<A> make_return(Arg&& arg) {
return std::make_tuple(std::move(arg));
}
void f(const std::tuple<A>&) { }
void f1() {
std::cout << "return local via make_tuple: ";
A a{};
f(std::make_tuple(a));
}
void f2() {
std::cout << "return local via make_tuple(move): ";
A a{};
f(std::make_tuple(std::move(a)));
}
void f3() {
std::cout << "return local via make_return: ";
A a{};
f(make_return(a));
}
void f4() {
std::cout << "return const via make_tuple: ";
const A a{};
f(std::make_tuple(a));
}
void f5() {
std::cout << "return const via make_tuple(move): ";
const A a{};
f(std::make_tuple(std::move(a)));
}
void f6() {
std::cout << "return const via make_return: ";
const A a{};
f(make_return(a));
}
int main() {
f1();
f2();
f3();
f4();
f5();
f6();
}
Выход:
return local via make_tuple: copy
return local via make_tuple(move): move
return local via make_return: move
return const via make_tuple: copy
return const via make_tuple(move): copy
return const via make_return: copy
В случаях, когда возвращается локальная неконстантная переменная, мы хотим, чтобы std::move
ее содержимое. Это можно std::make_tuple(std::move(a))
с помощью std::make_tuple(std::move(a))
, потому что будет скопирован простой std::make_tuple(a)
. Чтобы сохранить некоторую типизацию, автор написал make_return
как стенографию std::make_tuple(std::move(a))
: пример показывает, что f3
работает так же, как f2
.
Когда константа передается, std::move
не имеет никакого значения, но и никакого вреда. Таким образом, мы могли бы использовать std::make_tuple
, но make_return
работает отлично. Случаи f4
, f5
, f6
все ведут себя одинаково, показывая, что не нужно думать дважды, прежде чем смешивать константы и не константы в make_return
(в случае нескольких записей, составляющих return_t
).
Остается перемещение неконстантной переменной, не являющейся локальной функцией, и поэтому мы не хотели бы разрушать ее содержимое. В этих случаях make_return
нежелателен, и нужно вернуться к ручному вызову std::make_tuple
(используя std::move
если это необходимо).
Теперь, как это выглядит с помощью std::forward
? Изменение определения make_return
на использование
std::make_tuple(std::forward<Arg>(arg));
производит:
return local via tuple: copy
return local via tuple(move): move
return local via make_return: copy
return const via tuple: copy
return const via tuple(move): copy
return const via make_return: copy
так как a
in f3
передается как const A&
. Действительно, make_return
тогда, по логике пересылки, является простым синонимом для std::move
, теряя любую выгоду, которую мы надеялись достичь.