C++ C++;模板函数中的左值和右值

C++ C++;模板函数中的左值和右值,c++,move-semantics,rvalue,lvalue,C++,Move Semantics,Rvalue,Lvalue,我的样本来自: 但如果我想制作重载模板,比如: template<typename T> void overloaded (const T& x) {std::cout << "[lvalue]";} template<typename T> void overloaded (T&& x) {std::cout << "[rvalue]";} 有没有办法使用模板函数来扣除我转移到函数中的对象?有可能,只需在第二个重载中添加

我的样本来自:

但如果我想制作重载模板,比如:

template<typename T>
void overloaded (const T& x) {std::cout << "[lvalue]";}
template<typename T>
void overloaded (T&& x) {std::cout << "[rvalue]";}

有没有办法使用模板函数来扣除我转移到函数中的对象?

有可能,只需在第二个重载中添加一个
常量即可:

template<typename T>
void overloaded (const T& x);
template<typename T>
void overloaded (const T&& x);
//               ^^^^^

注意:如果您执行的特定操作对分支或其他分支无效,则需要使用
if constepr

每当函数模板参数具有类型“对类型模板参数的右值引用”,例如
模板void重载(T&&x)中的
x
,它将成为一个“转发参考”,也称为“通用参考”。它们遵循特殊规则,即它们可以匹配左值或右值参数

当参数为右值时,
T
的类型为非引用,参数类型为右值引用

当参数是左值时,
T
是左值引用类型。这是有意义的,因为还有第二条“引用折叠规则”:如果将
&
&
添加到已经是引用的类型别名(类型定义
typedef
、使用
定义的类型或类型模板参数)中,如果别名都是右值引用,并且您在其他三种情况下添加了
&&
,或左值引用,则它将形成一个引用类型,即右值引用

因此,当调用
重载(x)
时,当
x
int
类型的左值时,
重载的两个重载匹配:

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&);

现在给定任何左值参数,第二个模板将推断出
T
为左值引用类型,无法将其替换为返回类型,然后将忽略该模板以进行重载解析,以便可以使用第一个重载。

第二个
重载的
模板不用于右值,但它是一个转发参考。它在拾取调用时往往非常激进,因为它会准确地推断const-enss和value-ness,使其在几乎所有情况下都成为首选(您的int调用带有左值,但它不是const,所以它会被调用)

要使其工作,需要约束该模板。当该模板与左值匹配时,
T
将是引用类型,因此引用折叠规则表示
T&
将折叠为
T&
。因此,我们可以这样做:

template<typename T>
void overloaded (const T& x) {std::cout << "[lvalue]";}

template<typename T, std::enable_if_t<!std::is_reference<T>::value, int> = 0>
void overloaded (T&& x) {std::cout << "[rvalue]";}
模板
void重载(const T&x){std::cout

void重载(T&&x){std::cout您的
T&&
是一个通用参考。()你要寻找的是一个通用引用的完美转发。@sturcotte06它被称为转发引用:)被接受的答案确实不是你想要的…const是一个使特定示例工作的黑客,但它通常不是正确的。你需要做的是用enable_if约束模板。如果右值重载希望从参数中移除。是的,但您现在创建了一个新问题,该参数在函数体中是常量,它的评级是您想要的。这并不是解决此问题的真正方式。这很公平,但我认为OP不需要移动,因为这是示例代码。
template<typename T>
void overloaded (const T& x);
template<typename T>
void overloaded (const T&& x);
//               ^^^^^
template <typename T> void overloaded(T &&x) {
  if /*constexpr*/ (std::is_lvalue_reference_v<T>)
    std::cout << "[lvalue]";
  else
    std::cout << "[rvalue]";
}
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&);
#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>;
template<typename T>
void overloaded (const T& x) {std::cout << "[lvalue]";}

template<typename T, std::enable_if_t<!std::is_reference<T>::value, int> = 0>
void overloaded (T&& x) {std::cout << "[rvalue]";}