A small example reveals the author's intent:
#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();
}
Output:
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
In cases where a local non-const variable is returned, we want to std::move
its contents. This is achievable using std::make_tuple(std::move(a))
, because a plain std::make_tuple(a)
would copy. To save some typing, the author wrote make_return
as a shorthand for std::make_tuple(std::move(a))
: the example shows that f3
works just like f2
.
When a constant is passed, std::move
won't make any difference, but no harm either. So we could use std::make_tuple
, but make_return
works just fine, too. Cases f4
, f5
, f6
all behave the same, showing that one doesn't really need to think twice before mixing constants and non-constants in make_return
(in the case of multiple entries constituting return_t
).
What remains is moving a non-const variable that is not local to the function, and thus we wouldn't like to destroy its contents. In these cases make_return
is unwanted and one would need to resort back to manual invocation of std::make_tuple
(utilizing std::move
where appropriate only).
Now what would this look like with std::forward
? Changing the definition of make_return
to utilizing
std::make_tuple(std::forward<Arg>(arg));
produces:
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
since a
in f3
gets passed as a const A&
. Indeed, make_return
is then, by the logic of forwarding, a mere synonyme for std::move
, losing any benefit we hoped to achieve.
&&
other than for logical and." – Cecrops