Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/129.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 是否使用std::forward和auto&&;正确的做法_C++_C++11 - Fatal编程技术网

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);