C++ std::forward如何接收正确的参数?
考虑:C++ std::forward如何接收正确的参数?,c++,templates,c++11,rvalue-reference,perfect-forwarding,C++,Templates,C++11,Rvalue Reference,Perfect Forwarding,考虑: void g(int&); 无效g(int&&); 模板 无效f(T&x) { g(std::forward(x)); } int main() { f(10); } 既然id表达式x是一个左值,并且std::forward具有左值和右值的重载,为什么调用不绑定到接受左值的std::forward的重载 模板 constexpr T&forward(std::remove_reference_T&T)无例外; 为什么调用不绑定到采用左值的std::forward重载 它确实是这样做的
void g(int&);
无效g(int&&);
模板
无效f(T&x)
{
g(std::forward(x));
}
int main()
{
f(10);
}
既然id表达式x
是一个左值,并且std::forward
具有左值和右值的重载,为什么调用不绑定到接受左值的std::forward
的重载
模板
constexpr T&forward(std::remove_reference_T&T)无例外;
为什么调用不绑定到采用左值的std::forward
重载
它确实是这样做的,但是std::forward
它的模板参数,你告诉它它是什么类型的,这就是神奇之处。您正在向f()
传递一个pr值,因此f()
推断[T=int]
。然后它调用forward
的左值重载,由于forward
中发生的返回类型和静态转换
将属于int&
类型,因此调用void g(int&&)
重载
如果要将左值传递给f()
f()
推断出[T=int&]
,再次调用向前
的相同左值重载,但这次返回类型和静态转换
都是int&
,同样是因为引用折叠规则。这将调用void g(int&)
重载
Howard已经为您的问题提供了一个很好的答案,关于为什么需要
forward
的右值重载,但我将添加一个人为的示例,展示两个版本的实际情况
这两个重载背后的基本思想是,当传递给forward
的表达式的结果产生右值(prvalue或xvalue)时,将调用右值重载
假设您有一个类型foo
,它有一对ref限定的get()
成员函数重载。带有&
限定符的一个返回int
,而另一个返回int&
struct foo
{
int i = 42;
int get() && { return i; }
int& get() & { return i; }
};
然后说f()
对传递给它的任何对象调用get()
成员函数,并将其转发给g()
模板
自动f(T&T)
{
std::cout它确实绑定到std::forward
的重载,以获取左值:
template <class T>
constexpr T&& forward(remove_reference_t<T>& t) noexcept;
因为f
中的T
推导为int
。因此此重载将左值int
强制转换为xvalue,具体如下:
static_cast<int&&>(t)
抓住左值
过载2:
template <class T> constexpr T&& forward(remove_reference_t<T>&& t) noexcept;
template constepr T&&forward(删除引用)无例外;
捕获右值(即xvalues和prvalues)
重载1可以将其左值参数强制转换为左值或xvalue(为了重载解析的目的,后者将被解释为右值)
重载2只能将其右值参数强制转换为xvalue(出于重载解析的目的,它将被解释为右值)
重载2适用于中标记为“B.应将右值转发为右值”的情况。简而言之,此情况启用:
std::forward<T>(u.get());
std::forward(u.get());
如果不确定u.get()
是否返回左值或右值,但是如果T
不是左值引用类型,则希望移动返回的值。但不使用std::move
,因为如果T
是左值引用类型,则不希望从返回中移动
我知道这听起来有点做作。但是,设置激发动机的用例,说明std::forward
应该如何处理显式提供的模板参数和普通参数的隐式提供的表达式类别的所有组合,却遇到了很大的麻烦
这本书不容易阅读,但将模板和普通参数组合成std::forward
的基本原理仍在讨论中。当时,这在委员会上是有争议的,并不容易推销
std::forward
的最终形式并不完全符合建议。但是它确实通过了中介绍的所有六项测试。std::forward如何接收正确的参数?
为了实现完美的转发,如您的代码所示:
template<class T>
void f(T&& x)
{
g(std::forward<T>(x));
}
- 如果类型的左值的传递函数为
string
或string&
,则
转发函数\u Tp
将是字符串&
,\uu t
将是字符串&
等于字符串&
,将字符串&&
等于
字符串&
(引用折叠)
- 如果类型的传递函数右值为
string
或string&
,则
转发函数\u Tp
将是字符串,\u t
将是
字符串&
,将是字符串&
函数的作用是什么?
- 如果rvalue到rvalue(重载2)或lvalue到lvalue(重载1),则无需执行任何操作,只需返回
- 如果您不小心或其他原因,请将rvalue引导到lvalue,通过
static\u assert
(重载2)给您一个编译错误
- 如果将左值转换为右值(重载1),则与std::move相同,但不方便
正如Scott Meyers所说:
假设std::move和std::forward都归结为强制转换,那么
唯一的区别是std::move总是强制转换,而
std::forward有时会这样做,您可能会问我们是否可以
不要使用std::move,只要在任何地方使用std::forward即可。从
纯粹从技术角度来看,答案是肯定的:std::forward可以
这一切。std::move不是必需的。当然,这两个函数都不是必需的
真的很有必要,因为我们可以到处写演员,但我
希望我们同意这会很恶心
右值重载的作用是什么?
我不确定,它应该用在你真正需要的地方。比如Howard Hinnant说:
过载2用于外壳标签
template <class T>
constexpr T&& forward(remove_reference_t<T>& t) noexcept;
template <class T> constexpr T&& forward(remove_reference_t<T>&& t) noexcept;
std::forward<T>(u.get());
template<class T>
void f(T&& x)
{
g(std::forward<T>(x));
}
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{ return static_cast<_Tp&&>(__t); } //overload 1
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
{
static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
" substituting _Tp is an lvalue reference type");
return static_cast<_Tp&&>(__t);
}// overload 2