使用 std::forward with auto&& 是正确的做法
Is use of std::forward with auto&& the right thing to do
试图了解将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&&
类型的变量是通用引用,因为存在类型推导。这意味着uniRefRV
和uniRefLV
都是通用引用。
在我的例子中,很明显uniRefRV
是右值,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
你认为我们应该平等对待引用uniRefLV
和uniRefRV
吗?我们应该使用三个选项中的哪一个来实现完美转发?
您误解了指南。或者至少过于字面化了。
我认为这里有三件重要的事情需要认识。
首先,所有类型在这里都是已知的。通用引用的建议主要适用于带有模板的泛型代码,在这些模板中,您根本不知道某个东西是或接受了左值引用还是右值引用。
其次,函数接受一个右值引用:您必须传递一个右价。时期这里别无选择。
逻辑结论是,你不想传递一个通用引用:无论你传递什么,都必须是右值,它永远不可能是左值。通用引用可以是左值(如果它们被实例化为左值引用)。传递一个通用引用意味着"我不知道这是什么样的引用,我可以将其作为右值或左值传递,所以我传递它的方式与我得到的完全一样"。这个问题的情况更像是"我确切地知道我必须通过什么,所以这就是我将要通过的"。
让我们假设这个案例并不像看上去那么简单。代替giveMeInt
,我们有这样的东西:
template <typename T>
typename complex_computation<T>::type giveMeSomething(T t);
而不是moveMe
,它实际上具有通用引用,因此不需要无条件的std::move
。
template <typename T>
void wantForwarded(T&&);
现在您实际上需要完美的转发。
auto&& uniRef = giveMeSomething(iDontKnowTheTypeOfThis);
wantForwarded(std::forward<decltype(uniRef)>(uniRef);
我觉得这里有点混乱。uniRefLV
和uniRefRV
都不是"通用参考文献"。它们只是变量,都有一个确定的类型。这两种类型恰好都是引用类型,但这并不重要。
要调用moveWidget
,在这两种情况下都必须使用std::move
,并且在这两个情况下,语义都是在移动之后,原始的w
已经从中移动,因此不再处于确定状态。(你能做的最好的事情就是销毁它或重新分配给它。)
现在,相比之下,真正的通用引用是模板参数,并且它必须始终是规范形式:
template <typename T> void foo(T &&);
// ^^^^^^
foo(bar());
foo(x);
foo(std::move(y));
也就是说,
- 模板是函数模板
- 模板参数
T
作为函数参数T &&
出现,并且 - 实现专业化的模板论证是推导出的
当满足这些条件时,T &&
是通用引用,您可以通过std::forward<T>
传递它,从而保留正确的引用类型。
(1).a这并不违反准则。你特别说"我不再需要那个左值了,所以接受它"这基本上就是std::move的作用。如果您在完成std::继续使用uniRefLV,这是违反准则的。该值(可能)已消失,您将其忽略。斯科特就是这么说的。
(1).b这是合法的。虽然std::move在那里是不需要的,但它确实简化了读取源代码。
(2),(3)我们不必为std::forward提供模板参数。它将由编译器推导出来!至少我的理解是这样的。我认为手动操作没有意义。Forward只是从类型定义中移除的引用(&&和&)。这就是为什么它被称为转发并用于数据转发。当您有功能时:样板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);
不是:
void foo(Widget a);
因此,所有这些引用都被std::forward删除。只有*modificator* SomeType t
被提供给foo()
内部的任何函数。
- Problems with std::cin.fail()
- 应用程序崩溃并显示"symbol _ZdlPvm, version Qt_5 not defined in file libQt5Core.so.5 with link time reference"
- 完美前进使用 std::forward vs RefRefCast
- 这对"With a stackless coroutine, only the top-level routine may be suspended."意味着什么
- Boost.TEST with CLion: "Test framework quit unexpectedly"
- 避免碎片化的ClientHellos with OpenSSL (DTLS)
- Issues with Win32 ReadProcessMemory API
- Qt with WinAPI MouseProc
- [[maybe_unused]] with structured_binding?
- Issue with WriteProcessMemory
- OpenCV RTP-Stream with FFMPEG
- "Unable to start debugging. No process is associated with this object." - 在Visual Studio Code中使用GDB
- std::adjacent_difference with std::chrono time_point
- DLL Made with CMake 使程序崩溃
- QtCreator with C 库中的链接器问题
- SHBrowseForFolder with BIF_BROWSEFORCOMPUTER and SHGetPathFr
- specialized std::default_delete with QQmlComponent
- 你如何理解"std: :forward is just syntactic sugar"?这是真的吗?
- Returning std::make_pair with std::forward
- 使用 std::forward with auto&& 是正确的做法