This question already has answers here:
On how to recognize Rvalue or Lvalue reference and if-it-has-a-name rule
(3 answers)
Closed 6 years ago.
class MyClass {
public:
MyClass()
{
std::cout << "default constructor\n";
}
MyClass(MyClass& a)
{
std::cout << "copy constructor\n";
}
MyClass(MyClass&& b)
{
std::cout << "move constructor\n";
}
};
void test(MyClass&& temp)
{
MyClass a(MyClass{}); // calls MOVE constructor as expected
MyClass b(temp); // calls COPY constructor... not expected...?
}
int main()
{
test(MyClass{});
return 0;
}
For the above code, I expected both object creations in test() to call move constructor because b is a r-value reference type (MyClass&&).
However, passing b to the MyClasss constructor does not call move constructor as expected, but calls copy constructor instead.
Why does second case calls copy constructor even if the passed argument is of type MyClass&& (r-value reference)???
I am using gcc 5.2.1. To reproduce my results, you must pass -fno-elide-constructors option to gcc to disable copy elision optimization.
void test(MyClass&& temp)
{
if (std::is_rvalue_reference<decltype(temp)>::value)
std::cout << "temp is rvalue reference!!\n";
else
std::cout << "temp is NOT rvalue!!\n";
}
Above code prints out "temp is rvalue reference" even if temp is named.
temp's type is rvalue reference type.
void test(MyClass&& temp)
{
MyClass a(MyClass{}); // calls MOVE constructor as expected
MyClass b(temp); // calls COPY constructor... not expected...?
}
That is because, temp (since it has a name,) is an lvalue reference to an rvalue. To treat as an rvalue at any point, call on std::move
void test(MyClass&& temp)
{
MyClass a(MyClass{}); // calls MOVE constructor as expected
MyClass b(std::move(temp)); // calls MOVE constructor... :-)
}
Related
I have this simple piece of code in C++17 and I was expecting that the move constructor was called (or the copy constructor, if I was doing something wrong), while it is just calling the normal constructor and I cannot why is doing this optimization.
I am compiling with -O0 option.
#include <iostream>
using namespace std;
struct Foo {
int m_x;
Foo(int x) : m_x(x) { cout << "Ctor" << endl; }
Foo(const Foo &other) : m_x(other.m_x) { cout << "Copy ctor" << endl; }
Foo(Foo &&other) : m_x(other.m_x) {
other.m_x = 0;
cout << "Move ctor" << endl;
}
};
void drop(Foo &&foo) {}
int main() { drop(Foo(5)); }
I cannot why is doing this optimization.
This is not due to any optimization in C++17. Instead this is due to the fact that you're passing an int when you wrote Foo(5). And since you've provided a converting constructor Foo::Foo(int), it will be used to create a Foo object which will then be bound to the rvalue reference parameter of drop.
Note that in C++17, even if we were to make the parameter of drop to be of type Foo instead of Foo&&, then also there will be no call to the move constructor because of mandatory copy elison.
C++11
On the other hand, if you were using C++11 and using the flag -fno-elide-constructors and parameter to drop was of type Foo instead of Foo&& then you could see that a call would be made to the move ctor.
//--------vvv-----------> parameter is of type Foo instead of Foo&&
void drop(Foo foo) {
std::cout<<"drop called"<<std::endl;
}
int main() {
drop(Foo(5)); //in c++11 with -fno-elide-constructors the move ctor will be called
}
The output of the above modified version in C++11 with -fno-elide-constructors is:
Ctor
Move ctor
drop called
Demo
In function main you create a temporary Foo object from integer 5 but you don't move (nor copy) from it anywhere. To actually call your move (or copy) constructor, you have to move- (or copy-) construct another object from your temporary Foo.
E.g., to call Foo's move constructor:
void drop(Foo &&foo) {
// Move-construct tmp from foo.
Foo tmp { std::move(foo) };
}
This question already has answers here:
What is copy/move constructor choosing rule in C++? When does move-to-copy fallback happen?
(2 answers)
Closed 4 years ago.
Sample code, C++11, -fno-elide-constructors.
#include <iostream>
#include <string>
using namespace std;
class ClassA
{
int a, b;
public:
ClassA() = default;
ClassA(const ClassA &obj)
{
cout << "copy constructor called" << endl;
}
ClassA(ClassA &&obj) {
cout << "move ctor called" << endl;
}
};
ClassA bar(ClassA &str)
{
return str; //call copy ctor
}
int main()
{
ClassA str;
ClassA foo = bar(str); //call move ctor
return 0;
}
https://wandbox.org/permlink/DyALfRETs2LfWSjc
Why this assignment ClassA foo = bar(str); call move ctor instead of copy ctor? Since both const lvalue reference and rvalue reference can accept rvalue.
const lvalue reference can bind to everything, so if the compiler would favor this over the rvalue reference the move constructor would not be called ever if a copy constructor is provided, and that would defeat the purpose of having both.
The rvalue reference accepting function is a more specialized version of the more generic const lvalue reference accepting one, and the compiler always chooses the most specialized version.
This question already has answers here:
Why do I have to call move on an rvalue reference?
(2 answers)
Rvalue Reference is Treated as an Lvalue?
(4 answers)
Closed 5 years ago.
In the following (borrowed) example, in my environment the move constructor is never called:
#include <iostream>
class MyClass {
public:
MyClass()
{
std::cout << "default constructor\n";
}
MyClass(MyClass& a)
{
std::cout << "copy constructor\n";
}
MyClass(MyClass&& b)
{
std::cout << "move constructor\n";
}
};
void test(MyClass&& temp)
{
MyClass a(MyClass{}); // calls MOVE constructor as expected
MyClass b(temp); // calls COPY constructor...
}
int main()
{
test(MyClass{});
return 0;
}
My output is:
default constructor
default constructor
copy constructor
I use XCode version 9.1, shouldnt the move constructor be called on rvalue references? What am I missing here?
John.
What am I missing here?
The point is that everything that has a name is lvalue.
It means that named rvalue reference itself is lvalue and temp from:
void test(MyClass&& temp)
is lvalue as well. So move constructor is not called.
If you want a move constructor to be called, use std::move:
void test(MyClass&& temp)
{
// ...
MyClass b(std::move(temp)); // use std::move here
}
By the way,
MyClass(MyClass& a)
{
std::cout << "copy constructor\n";
}
is not a copy constructor because copy constructor has a form of:
MyClass(const MyClass& a) { ... }
#include <iostream>
using namespace std;
class Test {
public:
Test() {
cout << "Default constructor called." << endl;
}
Test(Test &obj) {
cout << "copy constructor called." << endl;
}
Test& operator=(const Test &obj) {
cout << "copy assignment called." << endl;
return *this;
}
~Test() {
cout << "destructor called." << endl;
}
};
Test func(Test test) {
cout << "func called" << endl;
return test;
}
int main(int argc, char* argv[]) {
Test t1;
Test t2 = func(t1); // No matching constructor for initialization for 'Test'
return 0;
}
I am learning c++. I wrote some test code to understand copy initialization and copy assignment. Now I can not figure out why the second assignment can not work.
An rvalue cannot be modified, and should, in this case, be treated as a const reference const Test& obj. So when you convert t1 into the test in func, it's OK because t1 is an lvalue, but not when you're constucting t2 from the return value, which is an xvalue (categorized into rvalue).
In short, the signature of your copy constructor is wrong because it only accepts lvalues.
The following patch on line 10 makes the code work for me.
Test(const Test &obj) {
^~~~~
Here's the article about copy constructor on cppreference.com. See Syntax section.
Also the rvalue article, which reads
An rvalue may be used to initialize a const lvalue reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.
P.S. You can use move semantics (C++11), too, which accept rvalues only. It's not hard to write a move constructor like this:
Test(Test&& obj) ...
Your copy constructor allows the object being copied to be modified.
The statement
Test t2 = func(t1);
will store the return value of func() in a temporary, and then copy that into t2. However, that requires a copy constructor that accepts a const reference, since a temporary cannot be bound to a non-const reference.
(Technically, the compiler is allowed to elide the temporary, but it is still required to issue diagnostics for your code assuming it has created the temporary. In other words, the copy constructor must be const).
Change your copy constructor to accept a const reference.
Test(Test const &obj) {
In the line commented by ***, why is Bar's copy constructor called? input_bar is a rvalue reference, so I expect the move constructor to be called. Did it convert to an lvalue reference? I can make the move constructor call if I change that line to bar_(std::move(input_bar)).
#include <iostream>
#include <array>
#include <memory>
class Bar
{
public:
Bar(const Bar& bar)
{
std::cout << "copy constructor called" << std::endl;
}
Bar(Bar&& bar)
{
std::cout << "move constructor called" << std::endl;
}
};
class Foo
{
public:
Foo(Bar&& input_bar) :
bar_(input_bar) // ***
{
}
Bar bar_;
};
int main()
{
Bar bar;
Foo foo(std::move(bar));
return 0;
}
Once an entity has a name, it is clearly an lvalue! If you have a name for an rvalue reference, the entity with the name is not an rvalue but an lvalue. The entire point is that you know that this entity references an rvalue and you can legitimately move its content.
If you want to just forward the rvalueness to the next function you call, you'd use std::move(), e.g.:
Foo(Bar&& bar): bar_(std::move(bar)) {}
Without the std::move() the rvalue is considered to be owned by the constructor. With the std::move() it releases the ownership and passes it on to the next function.
You have to move rhrs:
Foo(Bar&& input_bar) :
bar_(std::move(input_bar)) // ***
{
}
The reasoning is that once we actually use the RHR, it should be semantially treated as out of scope. Forcing you to use std::move allows the following code to not be undefined:
Foo(Bar&& input_bar) {
std::cout << input_bar.baz << std::endl;//not undefined
bar_ = Bar{std::move(input_bar)};//input_bar is now essentailly destroyted
//std::cout << input_bar.baz << std::endl;//Undefined behavior, don't do this
}
The general rule of thumb is that only things without names can actually be used as RHR's...RHR as arguments have names, and thus will be treated as LHR untill you call a function on them.