为什么 std::forward return static_cast<T&&> 而不是 static_cast<T>?
Why does std::forward return static_cast<T&&> and not static_cast<T>?
让我们有一个名为 Y 的函数重载:
void Y(int& lvalue)
{ cout << "lvalue!" << endl; }
void Y(int&& rvalue)
{ cout << "rvalue!" << endl; }
现在,让我们定义一个模板函数,其作用类似于 std::forward
template<class T>
void f(T&& x)
{
Y( static_cast<T&&>(x) ); // Using static_cast<T&&>(x) like in std::forward
}
现在看main()
int main()
{
int i = 10;
f(i); // lvalue >> T = int&
f(10); // rvalue >> T = int&&
}
正如预期的那样,输出是
lvalue!
rvalue!
现在回到模板函数 f()
并将 static_cast<T&&>(x)
替换为 static_cast<T>(x)
。让我们看看输出:
lvalue!
rvalue!
一样的!为什么?如果它们相同,那么为什么 std::forward<>
returns 从 x
转换为 T&&
?
左值与右值分类保持不变,但效果却大不相同(并且值类别确实发生了变化 - 尽管在您的示例中不是以可观察的方式)。让我们回顾一下这四种情况:
template<class T>
void f(T&& x)
{
Y(static_cast<T&&>(x));
}
template<class T>
void g(T&& x)
{
Y(static_cast<T>(x));
}
如果我们用左值调用 f
,T
将推断为某些 X&
,因此转换引用崩溃 X& && ==> X&
,所以我们最终得到相同的结果左值,没有任何变化。
如果我们用右值调用 f
,T
将推断为某些 X
,因此转换只是将 x
转换为对 [=18= 的右值引用], 所以它变成了一个右值(具体来说,一个 xvalue)。
如果我们用左值调用 g
,所有相同的事情都会发生。没有必要折叠引用,因为我们只是使用 T == X&
,但转换仍然是空操作,我们仍然得到相同的左值。
但是如果我们用右值调用g
,我们有static_cast<T>(x)
,它将复制x
。该副本 是 一个右值(正如您的测试所验证的那样 - 除了现在它是一个纯右值而不是一个 xvalue),但它充其量是一个额外的,不必要的副本并且会是编译失败(如果 T
是可移动但不可复制的)在最坏的情况下。使用 static_cast<T&&>(x)
,我们正在转换为不调用副本的引用。
这就是我们 T&&
的原因。
让我们有一个名为 Y 的函数重载:
void Y(int& lvalue)
{ cout << "lvalue!" << endl; }
void Y(int&& rvalue)
{ cout << "rvalue!" << endl; }
现在,让我们定义一个模板函数,其作用类似于 std::forward
template<class T>
void f(T&& x)
{
Y( static_cast<T&&>(x) ); // Using static_cast<T&&>(x) like in std::forward
}
现在看main()
int main()
{
int i = 10;
f(i); // lvalue >> T = int&
f(10); // rvalue >> T = int&&
}
正如预期的那样,输出是
lvalue!
rvalue!
现在回到模板函数 f()
并将 static_cast<T&&>(x)
替换为 static_cast<T>(x)
。让我们看看输出:
lvalue!
rvalue!
一样的!为什么?如果它们相同,那么为什么 std::forward<>
returns 从 x
转换为 T&&
?
左值与右值分类保持不变,但效果却大不相同(并且值类别确实发生了变化 - 尽管在您的示例中不是以可观察的方式)。让我们回顾一下这四种情况:
template<class T>
void f(T&& x)
{
Y(static_cast<T&&>(x));
}
template<class T>
void g(T&& x)
{
Y(static_cast<T>(x));
}
如果我们用左值调用 f
,T
将推断为某些 X&
,因此转换引用崩溃 X& && ==> X&
,所以我们最终得到相同的结果左值,没有任何变化。
如果我们用右值调用 f
,T
将推断为某些 X
,因此转换只是将 x
转换为对 [=18= 的右值引用], 所以它变成了一个右值(具体来说,一个 xvalue)。
如果我们用左值调用 g
,所有相同的事情都会发生。没有必要折叠引用,因为我们只是使用 T == X&
,但转换仍然是空操作,我们仍然得到相同的左值。
但是如果我们用右值调用g
,我们有static_cast<T>(x)
,它将复制x
。该副本 是 一个右值(正如您的测试所验证的那样 - 除了现在它是一个纯右值而不是一个 xvalue),但它充其量是一个额外的,不必要的副本并且会是编译失败(如果 T
是可移动但不可复制的)在最坏的情况下。使用 static_cast<T&&>(x)
,我们正在转换为不调用副本的引用。
这就是我们 T&&
的原因。