J. *_* S. 3 c++ rvalue lvalue move-semantics
我从http://www.cplusplus.com/reference/utility/forward中取样:
// forward example
#include <utility> // std::forward
#include <iostream> // std::cout
// function with lvalue and rvalue reference overloads:
void overloaded (const int& x) {std::cout << "[lvalue]";}
void overloaded (int&& x) {std::cout << "[rvalue]";}
// function template taking rvalue reference to deduced type:
template <class T> void fn (T&& x) {
overloaded (x); // always an lvalue
overloaded (std::forward<T>(x)); // rvalue if argument is rvalue
}
int main () {
int a;
std::cout << "calling fn with lvalue: ";
fn (a);
std::cout << '\n';
std::cout << "calling fn with rvalue: ";
fn (0);
std::cout << '\n';
return 0;
}
Run Code Online (Sandbox Code Playgroud)
打印
calling fn with lvalue: [lvalue][lvalue]
calling fn with rvalue: [lvalue][rvalue]
Run Code Online (Sandbox Code Playgroud)
但是,如果我想制作重载模板,那样:
template<typename T>
void overloaded (const T& x) {std::cout << "[lvalue]";}
template<typename T>
void overloaded (T&& x) {std::cout << "[rvalue]";}
Run Code Online (Sandbox Code Playgroud)
它打印
calling fn with lvalue: [rvalue][rvalue]
calling fn with rvalue: [rvalue][rvalue]
Run Code Online (Sandbox Code Playgroud)
有没有办法使用模板功能来推断我转移到哪个对象的功能?
是的,这是可能的,只需添加const到您的第二个重载:
template<typename T>
void overloaded (const T& x);
template<typename T>
void overloaded (const T&& x);
// ^^^^^
Run Code Online (Sandbox Code Playgroud)
您需要的原因const是x不进行转发引用.转发引用非常贪婪,如果你没有传入完全相同的类型(包括任何cv限定符),那么将选择转发引用重载.
在您的情况下,因为您没有传递const对象overload,所以第二次重载将始终是更好的匹配.
但是如果你在const那里添加,那么它不再是转发引用,并且只能接受rvalues而不是lvalues,并且不会更好地匹配左值作为结果,但是对于任何rvalues而言将比const&过载更好地匹配.
如果你需要离开x,那么你将不得不做其他事情.const&无论你有rvalue还是左值,都要删除转发引用中的重载和分支:
template <typename T> void overloaded(T &&x) {
if /*constexpr*/ (std::is_lvalue_reference_v<T>)
std::cout << "[lvalue]";
else
std::cout << "[rvalue]";
}
Run Code Online (Sandbox Code Playgroud)
注意:if constexpr如果您执行的某些特定内容对分支或其他分支无效,则需要使用.
只要函数模板参数的类型为“对类型模板参数的右值引用”,例如xin template<typename T> void overloaded(T&& x);,它就会成为“转发引用”,也称为“通用引用”。它们遵循特殊的规则,即它们可以匹配左值或右值参数。
当参数为右值时,T其类型为非引用,参数类型为右值引用。
当参数是左值时,T是左值引用类型。这是有道理的,因为有了第二条“引用折叠规则”:如果将a &或添加&&到已经是引用的类型别名(a typedef,用定义的using类型或类型模板参数),它将形成一个右值引用类型如果别名都是右值引用并且您添加&&,或者在其他三种情况下为左值引用,则引用。
因此,当您调用overloaded(x)when x是type的左值时int,两个overloadedmatch的重载:
template<typename T>
void overloaded (const T& x);
// Specialization void overloaded<int>(const int&);
template<typename T>
void overloaded (T&& x);
// Specialization void overloaded<int&>(int&);
Run Code Online (Sandbox Code Playgroud)
但是第二个专业化是参数类型的更好匹配,因此胜出。
为避免这种情况,我将使用SFINAE技术将第二种overloaded类型限制为仅非引用类型。
#include <type_traits>
// As before:
template<typename T>
void overloaded (const T& x);
template<typename T>
auto overloaded (T&& x) -> std::enable_if_t<!std::is_reference<T>::value>;
Run Code Online (Sandbox Code Playgroud)
现在给定任何左值参数,第二个模板将推导T为左值引用类型,无法将其替换为返回类型,然后将忽略该模板进行重载解析,以便可以使用第一个重载。