C++ 是否使用std::forward和auto&&;正确的做法
尝试了解将C++ 是否使用std::forward和auto&&;正确的做法,c++,c++11,C++,C++11,尝试了解将std::forward与auto&变量一起使用是否是传递这些变量以允许移动的正确方法 假设有一个函数: void moveWidget(Widget&& w); 和调用者-两个变量来表示右值和左值: Widget w; auto&& uniRefLV = w; // lvalue initialiser, // uniRefLV's type is Widget&
std::forward
与auto&
变量一起使用是否是传递这些变量以允许移动的正确方法
假设有一个函数:
void moveWidget(Widget&& w);
和调用者-两个变量来表示右值和左值:
Widget w;
auto&& uniRefLV = w; // lvalue initialiser,
// uniRefLV's type is Widget&
auto&& uniRefRV = std::move(w); // rvalue initialiser,
// uniRefRV's type is Widget&&
我们知道,auto&
类型的变量是一个通用的引用,因为存在类型推断。这意味着uniRefLV
和uniRefLV
都是通用参考
在我的示例中,uniRefLV
显然是右值,uniRefLV
是左值,但从概念上讲,它们都是通用的引用,如果定义不同,它们可以表示右值或左值
现在,我想调用moveWidget()
并完美地转发这些通用引用类型。该指南(作者Scott Meyers)说:
通过std::move
传递和返回右值引用,通过std::forward
传递和返回通用引用
除非我完全误解了指南,否则使用std::forward
似乎是合乎逻辑的。但是让我们考虑所有可能的选择:
// (1) std::move:
moveWidget(std::move(uniRefLV)); // Compiles and looks fine
// but violates the guideline?
// (unconditionally casts lvalue to rvalue)
moveWidget(std::move(uniRefRV)); // Same as above - but not an issue here
// as we cast rvalue to rvalue
// (2) std::forward with Widget:
moveWidget(std::forward<Widget>(uniRefLV)); // Compiles, follows the guideline
// but doesn't look right - what if
// we didn't know Widget's type?
moveWidget(std::forward<Widget>(uniRefRV)); // Same as above
// (3) std::forward with decltype:
moveWidget(std::forward<decltype(uniRefLV)>(uniRefLV)); // Fails to compile! (VC10)
// follows the guideline
// has nice and short syntax :)
moveWidget(std::forward<decltype(uniRefRV)>(uniRefRV)); // Compiles fine
/(1)标准::移动:
moveWidget(std::move(uniRefLV));//编译并看起来很好
//但是违反了准则?
//(无条件地将左值强制转换为右值)
moveWidget(std::move(uniRefRV));//同上-但这里不是问题
//当我们将右值转换为右值时
//(2)std::使用小部件转发:
moveWidget(std::forward(uniRefLV));//编写、遵循指南
//但是看起来不对劲-如果
//我们不知道Widget的类型?
moveWidget(std::forward(uniRefRV));//同上
//(3)std::带decltype的转发:
moveWidget(std::forward(uniRefLV));//编译失败!(VC10)
//遵循指导方针
//语法简洁明了:)
moveWidget(std::forward(uniRefRV));//编撰精良
您认为我们是否应该平等对待参考文献
uniRefLV
和uniRefRV
,以及我们应该使用三个选项中的哪一个来实现完美转发?您误解了指南。或者,至少从字面上看
我认为这里有三件重要的事情需要实现
首先,这里所有的类型都是已知的。通用引用的建议主要适用于带有模板的通用代码,在这些模板中,您根本不知道某个对象是左值引用还是右值引用
其次,函数采用右值引用:必须传递右值。时期这里别无选择
逻辑上的结论是,你不想传递一个通用的引用:无论你传递什么,都必须是右值,它永远不能是左值。通用引用可以是左值(如果它们被实例化为左值引用)。传递一个通用引用意味着“我不知道这是什么类型的引用,我可以把它作为右值或左值传递,所以我传递它的方式和我得到的完全一样”。这个问题中的情况更像是“我确切地知道我必须通过什么,所以这就是我将要通过的”。让我们假设这个情况并不像看起来那么简单。我们不是用
giveMeInt
而是这样:
template <typename T>
typename complex_computation<T>::type giveMeSomething(T t);
现在您实际上需要完美的转发
auto&& uniRef = giveMeSomething(iDontKnowTheTypeOfThis);
wantForwarded(std::forward<decltype(uniRef)>(uniRef);
auto&&uniRef=giveMeSomething(我不知道这种类型);
WANTFORWARD(标准::forward(uniRef);
我想你有点搞混了。无论是uniRefLV
还是uniRefRV
都不是“通用引用”。它们只是变量,并且都有一个确定的类型。这两种类型碰巧都是引用类型,但这并不重要
要调用moveWidget
,您必须在这两种情况下都使用std::move
,并且在这两种情况下,语义都是在移动之后,您的原始w
已从中移动,因此不再处于确定的状态。(您可以使用它的最佳方法是销毁它或重新分配给它。)
相比之下,真正的通用引用是一个模板参数,它必须始终是规范形式:
template <typename T> void foo(T &&);
// ^^^^^^
foo(bar());
foo(x);
foo(std::move(y));
模板无效foo(T&&);
// ^^^^^^
foo(bar());
foo(x);
foo(std::move(y));
就是
- 该模板是一个函数模板
- 模板参数
作为函数参数T
出现,并且T&
- 推导了实现专业化的模板参数
T&
是通用引用,您可以通过std::forward
传递它,它保留了正确的引用类型。(1)。a
它并不违反指导原则。你明确地说“我不再需要那个左值,所以接受它”这基本上就是std::move的目的。如果你在完成std::move之后继续使用uniRefLV,这是违反指导原则的。这个值消失了(可能),你就放弃了它。Scott就是这么说的
(1.b
这是合法的。虽然std::move在那里是不必要的,但它确实简化了源代码的读取
(2)、(3)
我们不必为std::forward提供模板参数。它应该由编译器来推导!至少我的理解是这样。我不认为手动执行它有什么意义。forward只需从类型定义中删除的引用(&&和&)。这就是为什么它被称为forwarding并用于数据转发。当您有函数时:
模板
void foo(T&T)
可将其实例化为以下任一项:
void foo(SomeType & t);
void foo(SomeType && t);
void foo(const SomeType & t);
void foo(const SomeType && t);
void foo(volatile SomeType & t);
void foo(volatile SomeType && t);
void foo(const volatile SomeType & t);
void foo(const volatile SomeType && t);
例如:
Widget a;
foo(a);
将实例化:
void foo(Widget & a);
不是:
所以所有这些引用都被std::forward.删除,只需*modifi
void foo(Widget & a);
void foo(Widget a);