C++中的函数参数rvalue引用

16

我正试图理解rvalue引用。我已经看到它们在构造函数中的使用方式,例如std::movestd::forward,但我仍然不理解为什么这种方法行不通:

void func(string&& str)
{
    cout << str << endl;
}
int main(int argc, char* argv[])
{
    string s("string");
    func(s);
}

这样做会:

template<typename T>
void func(T&& str)
{
    cout << str << endl;
}
int main(int argc, char* argv[])
{
    string s("string");
    func(s);
}

为什么使用函数模板版本会有效?


5
后一个版本的代码可行是因为 T 被推导为 string&。当函数参数类型形式为 T&& ,其中 T 是模板参数,并且函数参数是类型 A 的左值时,使用类型 A& 进行模板参数推导。如果想让前一个版本的代码可行,请使用 std::move - Peter Huene
这是模板推导的一个特殊情况,在这里(据我所知仅限于此),T 可以被推导为引用类型。通常在模板类型推导中,T 仅被推导为非引用类型。 - M.M
1
可能是通用引用的语法的重复问题。 - M.M
3个回答

16

像 @Peter 所说,T的类型被推断为 string&,而C++的引用折叠规则是:

T& & ⇒ T& //自C++98起
T&& & ⇒ T& // C++0x新增
T& && ⇒ T& // C++0x新增
T&& && ⇒ T&& // C++0x新增

所以,实际上 func 的实例化是:

void func(string& str)

而且它有效。


6
除了 @songyuanyao 的回答,以下是一些正式的解释:
根据 N4296::14.8.2.1 [temp.deduct.call],模板参数推导通过将每个函数模板参数类型(称为 P)与调用的相应参数的类型(称为 A)进行比较来完成。
根据 N4296::14.8.2.1/3 [temp.deduct.call],如果 P 是一个转发引用(即一个对未限定 cv 的模板参数的右值引用),并且参数是一个左值,则在类型推导中使用“左值引用到 A”代替 A。
标准还提供了以下示例:
template <class T> int f(T&& heisenreference);
template <class T> int g(const T&&);
int i;
int n1 = f(i); // calls f<int&>(int&)
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&)

这正是你的情况。

1
因为在模板内部,&&具有不同的含义,称为通用引用
带有&&参数(通用引用)的模板函数表示该参数可以用作引用或rvalue-reference。
在您的情况下,模板被推断为string&,这就是为什么它能够工作的原因。
要使用原始函数,您必须执行以下操作:
void func(string&& str)
{
    cout << str << endl;
}
int main(int argc, char* argv[])
{
    string s("string");
    func(std::move(s)); // move the string
    func(std::string("string")); // this is an rvalue and it is fine
}

关于通用引用的完整解释可以在这里找到: https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接