tem*_*boy 12 c++ templates rvalue-reference perfect-forwarding c++11
考虑:
void g(int&);
void g(int&&);
template<class T>
void f(T&& x)
{
g(std::forward<T>(x));
}
int main()
{
f(10);
}
Run Code Online (Sandbox Code Playgroud)
由于id-expression x是一个左值,并且std::forward对于左值和右值有重载,为什么调用不能绑定到std::forward需要左值的重载?
template<class T>
constexpr T&& forward(std::remove_reference_t<T>& t) noexcept;
Run Code Online (Sandbox Code Playgroud)
How*_*ant 15
它确实绑定了std::forward一个左值的重载:
template <class T>
constexpr T&& forward(remove_reference_t<T>& t) noexcept;
Run Code Online (Sandbox Code Playgroud)
它与之结合T == int.指定此函数返回:
static_cast<T&&>(t)
Run Code Online (Sandbox Code Playgroud)
因为T在f演绎到了int.所以这个重载将左值转换int为xvalue:
static_cast<int&&>(t)
Run Code Online (Sandbox Code Playgroud)
因此调用g(int&&)过载.
总之,lvalue重载std::forward可以将其参数转换为lvalue或rvalue,具体取决于T调用它的类型.
rvalue重载std::forward只能转换为rvalue.如果您尝试调用该重载并转换为左值,则程序格式错误(需要编译时错误).
超载1:
template <class T>
constexpr T&& forward(remove_reference_t<T>& t) noexcept;
Run Code Online (Sandbox Code Playgroud)
捕获左值.
超载2:
template <class T> constexpr T&& forward(remove_reference_t<T>&& t) noexcept;
Run Code Online (Sandbox Code Playgroud)
捕获rvalues(xvalues和prvalues).
重载1可以将其lvalue参数转换为lvalue或xvalue(后者将被解释为用于重载解析目的的rvalue).
Overload 2可以将其rvalue参数仅转换为xvalue(为了解决重载问题,它将被解释为rvalue).
过载2用于N2951中标记为"B.应将右值作为右值转发"的情况.简而言之,这种情况可以:
std::forward<T>(u.get());
Run Code Online (Sandbox Code Playgroud)
你不确定是否u.get()返回左值或左值,但如果T不是左值引用类型,你想要移动返回的值.但是,你不使用std::move,因为如果T 是一个左值引用类型,你不要想从返回移动.
我知道这听起来有点做作.然而,N2951在设置激励用例方面遇到了很大的麻烦,因为它std::forward应该如何处理显式提供的模板参数的所有组合,以及普通参数的隐式提供的表达式类别.
这不是一个简单的阅读,但模板和普通参数的每个组合的基本原理std::forward是在N2951中.当时这对委员会来说是有争议的,而不是轻易卖出.
最终的形式std::forward并不完全是N2951提出的.然而,它确实通过了N2951中提出的所有六个测试.
为什么调用不绑定到它的重载
std::forward需要一个左值?
它确实做到了这一点,但std::forward 没有推断出它的模板参数,而是告诉它它是什么类型,这就是魔术发生的地方。您正在将prvalue传递给f()这样f()推论[T = int]。然后,它调用的左值重载forward,并且由于引用使返回类型崩溃,并且在static_cast<T&&>其中发生的返回forward将是type int&&,因此调用了void g(int&&)重载。
如果您要将左值传递给 f()
int x = 0;
f(x);
Run Code Online (Sandbox Code Playgroud)
f()推导[T = int&],再次forward调用了相同的左值重载,但是这一次返回类型和static_cast<T&&>均为int&,再次是由于引用折叠规则。然后,这将调用void g(int&)重载。
Howard已经为您的问题(为什么需要rvalue重载)提供了一个很好的答案forward,但是我将添加一个人为设计的示例,其中显示了两个实际版本。
这两个重载背后的基本思想是,在传递给您的表达式的结果forward产生右值(prvalue或xvalue)的情况下,将调用rvalue重载。
假设您的类型foo具有一对ref限定的get()成员函数重载。一个带有&&限定符的返回一个int,另一个则返回int&。
struct foo
{
int i = 42;
int get() && { return i; }
int& get() & { return i; }
};
Run Code Online (Sandbox Code Playgroud)
然后say f()调用get()传递给它的成员函数,然后将其转发给g()
template<class T>
auto f(T&& t)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
g(forward<decltype(forward<T>(t).get())>(forward<T>(t).get()));
}
foo foo1;
f(foo1); // calls lvalue overload of forward for both calls to forward
f(std::move(foo1)); // calls lvalue overload of forward for forward<T>(t)
// but calls rvalue overload for outer call to forward
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1052 次 |
| 最近记录: |