您为什么要明确移动转发参考?
Why would you explicitly move a forwarding reference?
我正在查看一些代码,我看到了以下函数:
template <typename... Args>
static return_t make_return(Args &&... args)
{
// using std::forward<Args> will preserve lvalue args as such, but the point of this function
// is to make a return, where the 99.9+% case is moving a local (lvalue) into the return pack.
// Thus it forces a move, which will move `T&` args (but _not_ `const T&` args) into the
// return pack so that users don't need to type out a bunch of std::moves themselves/
// If they don't want implicit move they can just call std::make_tuple directly
return std::make_tuple(std::move(args)...);
}
这里的文档让我很困惑。
您为什么要明确移动转发参考?
您不想在通用上下文中保留左值/右值吗?
我无法理解基本原理或此行为与推荐的 std::forward
.
有何不同
换句话说,
我从未见过有人明确选择不完美转发转发参考。
有道理吗?
Why would you explicitly move a forwarding reference?
因为它没有被用于转发属性。是的,你是对的,我们通常 std::forward
一个转发参考。但在这种情况下,作者使用转发引用只是为了方便。如果它写成 make_return(Args&... args)
那么就不可能将右值传递给 make_return
,因为非常量左值引用可能不会绑定到一个
通过使用转发引用,作者允许将任何值类别的值传递到函数中,如果需要 none 则不会产生额外的副本。文档在那里澄清函数签名不是用于转发,而只是用于绑定到它给出的任何参数以移出。
一个小例子揭示了作者的意图:
#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(a)
会复制。为了节省一些打字时间,作者将 make_return
写成 std::make_tuple(std::move(a))
的 shorthand:该示例表明 f3
的工作方式与 f2
相同。
当传递常量时,std::move
不会有任何区别,但也没有坏处。所以我们可以使用 std::make_tuple
,但 make_return
也可以正常工作。案例 f4
、f5
、f6
都表现相同,表明在 make_return
(在多个条目构成 return_t
).
的情况
剩下的就是移动一个非函数本地的非常量变量,因此我们不想破坏它的内容。在这些情况下 make_return
是不需要的,需要重新手动调用 std::make_tuple
(在适当的地方使用 std::move
only)。
现在 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
因为 f3
中的 a
作为 const A&
传递。事实上,make_return
只是转发的逻辑,只是 std::move
的同义词,失去了我们希望获得的任何好处。
make_return()
是 return 使用 value 的元组,因为这个 value 不会不再需要,正如在 make_return(函数的结束范围)中使用的那样,没有必要使用 std::forward<>
,因为它可以转发在副本中产生的左值引用(取决于实现),但是 值 处于范围末尾,因此不需要保留任何资源。
在 make_tuple 上强制 std::move
,强制首先使用右值引用,省略可能的更多开销(取决于实现)。
我正在查看一些代码,我看到了以下函数:
template <typename... Args>
static return_t make_return(Args &&... args)
{
// using std::forward<Args> will preserve lvalue args as such, but the point of this function
// is to make a return, where the 99.9+% case is moving a local (lvalue) into the return pack.
// Thus it forces a move, which will move `T&` args (but _not_ `const T&` args) into the
// return pack so that users don't need to type out a bunch of std::moves themselves/
// If they don't want implicit move they can just call std::make_tuple directly
return std::make_tuple(std::move(args)...);
}
这里的文档让我很困惑。
您为什么要明确移动转发参考?
您不想在通用上下文中保留左值/右值吗?
我无法理解基本原理或此行为与推荐的 std::forward
.
换句话说,
我从未见过有人明确选择不完美转发转发参考。
有道理吗?
Why would you explicitly move a forwarding reference?
因为它没有被用于转发属性。是的,你是对的,我们通常 std::forward
一个转发参考。但在这种情况下,作者使用转发引用只是为了方便。如果它写成 make_return(Args&... args)
那么就不可能将右值传递给 make_return
,因为非常量左值引用可能不会绑定到一个
通过使用转发引用,作者允许将任何值类别的值传递到函数中,如果需要 none 则不会产生额外的副本。文档在那里澄清函数签名不是用于转发,而只是用于绑定到它给出的任何参数以移出。
一个小例子揭示了作者的意图:
#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(a)
会复制。为了节省一些打字时间,作者将 make_return
写成 std::make_tuple(std::move(a))
的 shorthand:该示例表明 f3
的工作方式与 f2
相同。
当传递常量时,std::move
不会有任何区别,但也没有坏处。所以我们可以使用 std::make_tuple
,但 make_return
也可以正常工作。案例 f4
、f5
、f6
都表现相同,表明在 make_return
(在多个条目构成 return_t
).
剩下的就是移动一个非函数本地的非常量变量,因此我们不想破坏它的内容。在这些情况下 make_return
是不需要的,需要重新手动调用 std::make_tuple
(在适当的地方使用 std::move
only)。
现在 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
因为 f3
中的 a
作为 const A&
传递。事实上,make_return
只是转发的逻辑,只是 std::move
的同义词,失去了我们希望获得的任何好处。
make_return()
是 return 使用 value 的元组,因为这个 value 不会不再需要,正如在 make_return(函数的结束范围)中使用的那样,没有必要使用 std::forward<>
,因为它可以转发在副本中产生的左值引用(取决于实现),但是 值 处于范围末尾,因此不需要保留任何资源。
在 make_tuple 上强制 std::move
,强制首先使用右值引用,省略可能的更多开销(取决于实现)。