C ++ why passing lvalue to move constructor works for templates?

I have this code that does not compile, which is expected.

This is a mistake: an rvalue reference cannot be bound to an lvalue

 class SomeData { public: vector<int> data; SomeData() { cout << "SomeData ctor" << endl; data.push_back(1); data.push_back(2); data.push_back(3); } SomeData(const SomeData &other) { cout << "SomeData copy ctor" << endl; data = other.data; } SomeData(SomeData &&other) { cout << "SomeData move ctor" << endl; data = move(other.data); } ~SomeData() { cout << "SomeData dtor" << endl; } void Print() const { for(int i : data) cout << i; cout << endl; } }; void Function(SomeData &&someData) { SomeData localData(someData); localData.Print(); } int main(int argc, char *argv[]) { SomeData data; Function(data); // ERROR data.Print(); return 0; } 

But when I turn Function() into a template, it works fine and uses the SomeData copy constructor SomeData .

 template<class T> void Function(T &&someData) { T localData(someData); // no more error localData.Print(); } 


Is this the standard C ++ behavior?

I noticed that visual studio tends to be more forgiving when it comes to templates, so I wonder if I can expect the same behavior from all compatible C ++ 11 compilers.

+6
source share
3 answers

Yes. In the case of the template function, the compiler outputs the argument of the template T so that it matches the specified argument.

Since someData is actually an lvalue, T is output as SomeData & . Function declaration, after type inference, then becomes

 void Function(SomeData & &&) 

and SomeData & && , following the rules for link folding , becomes SomeData & .

Therefore, the argument to someData becomes an lvalue reference and is passed as such to initialize localData . Note that (as @aschepler pointed out correctly) localData declared as T , so it is itself a reference of type SomeData & . Therefore, no copy construct occurs here - just link initialization.


If you want localData be an actual copy, you would need to rotate the type from SomeData & to someData , i.e. you have to remove & from this type. You can do this using std::remove_reference :

 template<class T> void Function(T &&someData) { /* Create a copy, rather than initialize a reference: */ typename std::remove_reference<T>::type localData(someData); localData.Print(); } 

(For this, you need #include <type_traits> .)

+9
source

This is really intended behavior. Template form functions:

 template< class T > Ret Function( T&& param ) { ... } 

follows special rules (Ret may or may not be a pattern, it does not matter). T&& is called a universal reference, and it can basically be attached to everything. This is because when the template calculation is triggered and the parameter is in this form (beware, vector<T>&& not a universal reference, and is not C<T> for any other template parameter), the drop rule is applied links:

T = int & => T && = int && & and which collapse to one int &

full correlation table:

 & + && = & && + & = & && + && = && 

So, when you have the above function

 int a = 5; int b& = a; Function( a ); // (1) Function( b ); // (2) Function( 3 ); // (3) 

In case 1, T = int & and the type being deduced is int & (since a is an lvalue), so the function has the following signature:

 Ret Function( int& param ) // int& && collapses to int& 

In case 2, T = int &

 Ret Function( int& param ) // int& && collapses to int& 

In case 3, T = int

 Ret Function( int&& param ) 

This rule of collapse is what the committee deemed reasonable to do the perfect forwarding work. You can find the long story in this Scott Meyer video

+4
source

Just to reassure you: this is not a problem with MSVC, this is really the expected behavior.

In && templates, it has a different meaning when applied to template type parameters. It is called a universal link .

I could probably rephrase it, but this article explains it very nicely, so you should just read it.

Summarizing (and very inaccurate), universal links have the ability to "break up" into regular links, if the need arises.

+2
source

Source: https://habr.com/ru/post/949621/


All Articles