为了账号安全,请及时绑定邮箱和手机立即绑定

为什么使用std :: forward禁用模板自变量推导?

为什么使用std :: forward禁用模板自变量推导?

C++
慕的地8271018 2019-10-31 13:00:34
在VS2010中,std :: forward的定义如下:template<class _Ty> inline_Ty&& forward(typename identity<_Ty>::type& _Arg){   // forward _Arg, given explicitly specified type parameter    return ((_Ty&&)_Arg);}identity似乎仅用于禁用模板参数推导。在这种情况下有意禁用它有什么意义?
查看完整描述

3 回答

?
智慧大石

TA贡献1946条经验 获得超3个赞

如果将对类型对象的右值引用传递给以X类型T&&为参数的模板函数,则模板参数推导将推导T为X。因此,该参数具有type X&&。如果函数参数是左值或const左值,则编译器会推导其类型为该类型的左值引用或const左值引用。


如果std::forward使用模板参数推导:


由于objects with names are lvalues只有输入参数是未命名的右值(例如或),std::forward才能正确转换T&&为。在完美转发的情况下,您传递给的是左值,因为它具有名称。的类型将推导为左值引用或const左值引用。引用折叠规则将导致in std :: forward 中的in始终解析为左值引用或const左值引用。7func()argstd::forwardstd::forwardT&&static_cast<T&&>(arg)


例:


template<typename T>

T&& forward_with_deduction(T&& obj)

{

    return static_cast<T&&>(obj);

}


void test(int&){}

void test(const int&){}

void test(int&&){}


template<typename T>

void perfect_forwarder(T&& obj)

{

    test(forward_with_deduction(obj));

}


int main()

{

    int x;

    const int& y(x);

    int&& z = std::move(x);


    test(forward_with_deduction(7));    //  7 is an int&&, correctly calls test(int&&)

    test(forward_with_deduction(z));    //  z is treated as an int&, calls test(int&)


    //  All the below call test(int&) or test(const int&) because in perfect_forwarder 'obj' is treated as

    //  an int& or const int& (because it is named) so T in forward_with_deduction is deduced as int& 

    //  or const int&. The T&& in static_cast<T&&>(obj) then collapses to int& or const int& - which is not what 

    //  we want in the bottom two cases.

    perfect_forwarder(x);           

    perfect_forwarder(y);           

    perfect_forwarder(std::move(x));

    perfect_forwarder(std::move(y));

}


查看完整回答
反对 回复 2019-10-31
?
泛舟湖上清波郎朗

TA贡献1818条经验 获得超3个赞

因为std::forward(expr)没有用。它唯一能做的就是无操作,即完美地推论其论点并像身份函数一样工作。替代方法是与相同std::move,但我们已经有了。换句话说,假设有可能,


template<typename Arg>

void generic_program(Arg&& arg)

{

    std::forward(arg);

}

std::forward(arg)在语义上等效于arg。另一方面,std::forward<Arg>(arg)在一般情况下,这不是禁忌。


因此,通过禁止std::forward(arg)它可以帮助捕获程序员错误,并且由于的任何可能的使用std::forward(arg)都被代替了,因此我们什么也不会丢失arg。


我认为,如果我们专注于确切std::forward<Arg>(arg) 执行的事情,而不是std::forward(arg)会执行的事情,您会更好地理解(因为这是无趣的禁忌)。让我们尝试编写一个完美地传递其参数的无操作函数模板。


template<typename NoopArg>

NoopArg&& noop(NoopArg&& arg)

{ return arg; }

这种幼稚的第一次尝试不是很有效。如果我们调用,noop(0)则NoopArg推导为int。这意味着返回类型为int&&并且我们不能从表达式中绑定这样的右值引用arg,即左值(它是参数的名称)。如果然后尝试:


template<typename NoopArg>

NoopArg&& noop(NoopArg&& arg)

{ return std::move(arg); }

然后int i = 0; noop(i);失败。这次NoopArg被推导出为int&(引用折叠规则保证将int& &&折叠为int&),因此返回类型为int&,这一次我们不能从std::move(arg)作为xvalue 的表达式中绑定这种左值引用。


在像的完美转发函数的上下文中noop,有时我们想移动,而其他时候我们不想要。知道是否应该移动的规则取决于Arg:如果不是左值引用类型,则意味着noop传递了右值。如果是左值引用类型,则表示noop已传递左值。因此std::forward<NoopArg>(arg),为了使功能模板做正确的事情,NoopArg是中的一个必要参数std::forward。没有它,就没有足够的信息。这NoopArg是不是同一类型什么T的参数std::forward会在一般情况下推断。


查看完整回答
反对 回复 2019-10-31
?
杨魅力

TA贡献1811条经验 获得超6个赞

由于我们将变量传递给std::forwardT将被推导为左值引用类型,并且引用折叠规则将确保返回类型与该左值引用类型相同,因此该调用将为左值。同样,std::forward(0)将是一个xvalue。因此,推论std::forward将完美地提出其论点,并且是一个无人问津。

查看完整回答
反对 回复 2019-10-31
  • 3 回答
  • 0 关注
  • 570 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信