std :: forward如何工作?

我知道它的作用以及何时使用它,但是我仍然无法确定它是如何工作的。请尽可能详细,并说明std::forward如果允许使用模板参数推导的话,什么时候不正确。

我的部分困惑是:“如果有名称,则是左值”-如果是这种情况,为什么std::forward在通过thing&& xvs 时行为会有所不同thing& x


噜噜哒
浏览 614回答 3
3回答

拉丁的传说

首先,让我们看一下std::forward根据标准执行的操作:§20.2.3 [forward] p2返回值: static_cast<T&&>(t)(这T是显式指定的模板参数,t它是传递的参数。)现在,请记住参考折叠规则:TR&nbsp; &nbsp;RT&&nbsp; &nbsp;&&nbsp; -> T&&nbsp; // lvalue reference to cv TR -> lvalue reference to TT&&nbsp; &nbsp;&& -> T&&nbsp; // rvalue reference to cv TR -> TR (lvalue reference to T)T&&&nbsp; &&nbsp; -> T&&nbsp; // lvalue reference to cv TR -> lvalue reference to TT&&&nbsp; && -> T&& // rvalue reference to cv TR -> TR (rvalue reference to T)(从这个答案中偷偷偷走了。)然后,让我们看一下一个想要使用完美转发的类:template<class T>struct some_struct{&nbsp; T _v;&nbsp; template<class U>&nbsp; some_struct(U&& v)&nbsp; &nbsp; : _v(static_cast<U&&>(v)) {} // perfect forwarding here&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// std::forward is just syntactic sugar for this};现在是一个示例调用:int main(){&nbsp; some_struct<int> s1(5);&nbsp; // in ctor: '5' is rvalue (int&&), so 'U' is deduced as 'int', giving 'int&&'&nbsp; // ctor after deduction: 'some_struct(int&& v)' ('U' == 'int')&nbsp; // with rvalue reference 'v' bound to rvalue '5'&nbsp; // now we 'static_cast' 'v' to 'U&&', giving 'static_cast<int&&>(v)'&nbsp; // this just turns 'v' back into an rvalue&nbsp; // (named rvalue references, 'v' in this case, are lvalues)&nbsp; // huzzah, we forwarded an rvalue to the constructor of '_v'!&nbsp; // attention, real magic happens here&nbsp; int i = 5;&nbsp; some_struct<int> s2(i);&nbsp; // in ctor: 'i' is an lvalue ('int&'), so 'U' is deduced as 'int&', giving 'int& &&'&nbsp; // applying the reference collapsing rules yields 'int&' (& + && -> &)&nbsp; // ctor after deduction and collapsing: 'some_struct(int& v)' ('U' == 'int&')&nbsp; // with lvalue reference 'v' bound to lvalue 'i'&nbsp; // now we 'static_cast' 'v' to 'U&&', giving 'static_cast<int& &&>(v)'&nbsp; // after collapsing rules: 'static_cast<int&>(v)'&nbsp; // this is a no-op, 'v' is already 'int&'&nbsp; // huzzah, we forwarded an lvalue to the constructor of '_v'!}我希望这个循序渐进的答案可以帮助您和其他人了解其std::forward工作原理。

繁星点点滴滴

我认为std::forwardas 的解释static_cast<T&&>令人困惑。我们对转换的直觉是将类型转换为其他类型-在这种情况下,它将是对右值引用的转换。不是!因此,我们正在使用另一种神秘事物来解释一种神秘事物。这种特殊的演员表由Xeo的答案中的表格定义。但是问题是:为什么?所以这是我的理解:假设我想向您传递一个std::vector<T> v您应该作为数据成员存储在数据结构中的数据_v。天真的(和安全的)解决方案是始终将向量复制到其最终目的地。因此,如果通过中间函数(方法)执行此操作,则应将该函数声明为引用。(如果将其声明为按值获取向量,则将执行其他完全不必要的复制。)void set(const std::vector<T> & v) { _v = v; }如果您手里有一个左值,这一切都很好,但是右值呢?假设向量是调用函数的结果makeAndFillVector()。如果您执行直接分配:_v = makeAndFillVector();编译器将移动向量而不是复制向量。但是,如果您引入中介,set()则有关您的论据右值性质的信息将丢失,并将进行复制。set(makeAndFillVector()); // set will still make a copy为了避免这种复制,您需要“完美转发”,这将每次都能获得最佳代码。如果给定左值,则希望函数将其视为左值并进行复制。如果给定了右值,则希望函数将其视为右值并将其移动。通常,您可以通过set()分别为lvalues和rvalues 重载函数来做到这一点:set(const std::vector<T> & lv) { _v = v; }set(std::vector<T> && rv) { _v = std::move(rv); }但是现在想象一下,您正在编写一个接受T和调用set()该函数的模板函数T(不必担心我们set()仅为向量定义的事实)。诀窍在于,set()当模板函数使用左值实例化时,您希望此模板调用第一个版本,而当使用右值初始化时,则希望该模板调用第二个版本。首先,该函数的签名应该是什么?答案是这样的:template<class T>void perfectSet(T && t);根据调用此模板函数的方式,T将在某种程度上以不同的方式神奇地推断出类型。如果使用左值调用它:std::vector<T> v;perfectSet(v);向量v将通过引用传递。但是,如果使用右值调用它:perfectSet(makeAndFillVector());(匿名)向量将通过右值引用传递。因此,故意设置C ++ 11魔术以尽可能保留参数的右值性质。现在,在perfectSet内部,您希望将参数完美地传递给的正确重载set()。这std::forward是必需的:template<class T>void perfectSet(T && t) {&nbsp; &nbsp; set(std::forward<T>(t));}如果没有std :: forward,编译器将不得不假定我们要通过引用传递t。为了使自己确信这是真的,请比较以下代码:void perfectSet(T && t) {&nbsp; &nbsp; set(t);&nbsp; &nbsp; set(t); // t still unchanged}对此:void perfectSet(T && t) {&nbsp; &nbsp; set(std::forward<T>(t));&nbsp; &nbsp; set(t); // t is now empty}如果您未明确转发t,则编译器必须防御性地假设您可能会再次访问t并选择set的左值引用版本。但是,如果转发t,则编译器将保留其右值性,并且set()将调用的右值引用版本。此版本移动的内容t,这意味着原始内容为空。这个答案比我最初认为的要长得多;-)
打开App,查看更多内容
随时随地看视频慕课网APP