CarlosHD
CarlosHD

Reputation: 303

C++, rvalue references in function parameters

I'm trying to understand rvalue references. I have seen how they are used in constructors, with things like std::move and std::forward, but I still don't understand why this doesn't work:

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

And this does:

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

Why does it work with the function template version?

Upvotes: 15

Views: 4612

Answers (3)

Moia
Moia

Reputation: 2344

Because inside of a template && has a different meaning, it's called universal reference.

a template function with a && parameter (unversal reference) means the parmeter can be used as reference or rvalue-reference.

In your case the template is deduced as string& and that's why it works.

To work with the raw function you have to do this:

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
}

A complete explanation about universal references can be found here: https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers

Upvotes: 1

songyuanyao
songyuanyao

Reputation: 172864

Like @Peter said, the type of T is deduced as string&, and C++’s reference-collapsing rule says:

T& & ⇒ T& // from C++98
T&& & ⇒ T& // new for C++0x
T& && ⇒ T& // new for C++0x
T&& && ⇒ T&& // new for C++0x

So func’s instantiation is actually:

void func(string& str)

And it works.

Upvotes: 15

user2953119
user2953119

Reputation:

Some formal explanation in addition to @songyuanyao's answer:

N4296::14.8.2.1 [temp.deduct.call]:

Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below.

N4296::14.8.2.1/3 [temp.deduct.call]:

A forwarding reference is an rvalue reference to a cv-unqualified template parameter. If P is a forwarding reference and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.

The Standard also provides the following example:

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

That's exactly your case.

Upvotes: 6

Related Questions