C++, rvalue references in function parameters
Asked Answered
I

3

15

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?

Ihram answered 20/5, 2015 at 3:47 Comment(3)
The latter version works because T is deduced to be string&. "When the function parameter type is of the form T&& where T is a template parameter, and the function argument is an lvalue of type A, the type A& is used for template argument deduction." Use std::move if you want the former to work.Allegraallegretto
This is a special case of template deduction, here (and only here AFAIK), T can be deduced to a reference type. Normally in template type deduction T is only deduced as a non-reference.Cop
possible duplicate of Syntax for universal referencesCop
F
15

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.

Felisha answered 20/5, 2015 at 4:7 Comment(0)
S
6

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.

Sheree answered 20/5, 2015 at 5:30 Comment(0)
W
1

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

Wakeup answered 12/4, 2019 at 7:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.