This question already has answers here:
Why should the copy constructor accept its parameter by reference in C++?
(9 answers)
Closed 8 years ago.
I met a quiz saying that the code below is ill-formed because "It is illegal to have a constructor whose first and only non-default argument is a value parameter for the class type."
I couldn't understand that. Why things like A(A a) : val (a.val) {} is ruled as illegal? why such a line in the standard? Is it because this will lead to ambiguity with copy constructor?
#include <iostream>
struct A
{
A() : val() {}
A(int v) : val(v) {}
A(A a) : val(a.val) {}
int val;
};
int main(int argc, char** argv)
{
A a1(5);
A a2(a1);
std::cout << a1.val + a2.val << std::endl;
return 0;
}
A(A a) : val(a.val) {} will cause an infinite recursion because constructor arguments are being copied by value (invoking copy constructor and again the copy constructor and ...)
A copy constructor is called when an object is passed by value. Copy constructor itself is a function. So if we pass argument by value in a copy constructor, a call to copy constructor would be made to call copy constructor which becomes a non-terminating chain of calls. Therefore compiler doesn’t allow parameters to be pass by value
Its already discussed on this SO post
Why should the copy constructor accept its parameter by reference in C++?
Everything changes with const:
#include <iostream>
struct A
{
A () : _a(0) {}
A (int a) : _a(a) {}
A (const A& a) : _a(a._a) {}
int _a;
};
int main()
{
A a(5);
A b(10);
A c(a);
std::cout << a._a + b._a + c._a << std::endl; // 20
return 0;
}
Related
From what I learned, I thought Foo a = 1 is equivalent to Foo a = (Foo)1.
With copy constructor declared, yes, they both result in calling Converting constructor.
class Foo
{
public:
// Converting constructor
Foo(int n) : value_(n) { std::cout << "Converting constructor." << std::endl; }
// Copy constructor
Foo(const Foo&) { std::couut << "Copy constructor." << std::endl; }
private:
int value_;
};
int main()
{
Foo a = 1; // OK - prints only "Converting constructor."
Foo b = (Foo)1; // OK - prints only "Converting constructor."
}
In contrast, without copy constructor, it doesn't compile even though it doesn't ever call copy constructor.
class Foo
{
public:
// Converting constructor
Foo(int n) : value_(n) { std::cout << "Converting constructor." << std::endl; }
// Copy constructor deleted
Foo(const Foo&) = delete;
private:
int value_;
};
int main()
{
Foo a = 1; // OK - prints only "Converting constructor."
Foo b = (Foo)1; // Error C2280: 'Foo::Foo(const Foo &)': attempting to reference a deleted function
}
What makes difference?
Thank you.
Foo a = 1; calls Foo(int n). (Foo)1 also calls Foo(int n). Foo b = (Foo)1; calls copy constructor Foo(const Foo&). Until C++17 the last call can be elided but the copy constructor must be available. Since C++17 copy elision is mandatory and the copy constructor isn't necessary: https://en.cppreference.com/w/cpp/language/copy_elision
Probably you're using a C++ standard before C++17 and the copy constructor is required but not called.
"From what I learned, I thought Foo a = 1 is equivalent to Foo a = (Foo)1." That's not equivalent. The first is one constructor call and the second is two constructor calls but one call can/must be elided.
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) { ... }
I wrote the following c++ code trying to understand copy elision in c++.
#include <iostream>
using namespace std;
class B
{
public:
B(int x ) //default constructor
{
cout << "Constructor called" << endl;
}
B(const B &b) //copy constructor
{
cout << "Copy constructor called" << endl;
}
};
int main()
{
B ob =5;
ob=6;
ob=7;
return 0;
}
This produces the following output:
Constructor called
Constructor called
Constructor called
I fail to understand why is the constructor being called thrice with each assignment to object ob.
B ob =5;
This uses the given constructor.
ob=6;
This uses the given constructor because there is not a B& operator=(int) function and 6 must be converted to type B. One path to do this is to temporarily construct a B and use it in the assignment.
ob=7;
Same answer as above.
I fail to understand why is the constructor being called thrice with each assignment
As I stated above you do not have a B& operator=(int) function but the compiler is happy to provide a copy assignment operator (i.e., B& operator=(const B&);) for you automatically. The compiler generated assignment operator is being called and it takes a B type and all int types can be converted to a B type (via the constructor you provided).
Note: You can disable the implicit conversion by using explicit (i.e., explicit B(int x);) and I would recommend the use of explicit except when implicit conversions are desired.
Example
#include <iostream>
class B
{
public:
B(int x) { std::cout << "B ctor\n"; }
B(const B& b) { std::cout << B copy ctor\n"; }
};
B createB()
{
B b = 5;
return b;
}
int main()
{
B b = createB();
return 0;
}
Example Output
Note: Compiled using Visual Studio 2013 (Release)
B ctor
This shows the copy constructor was elided (i.e., the B instance in the createB function is triggered but no other constructors).
Each time you assign an instance of the variable ob of type B an integer value, you are basically constructing a new instance of B thus calling the constructor. Think about it, how else would the compiler know how to create an instance of B if not through the constructor taking an int as parameter?
If you overloaded the assignment operator for your class B taking an int, it would be called:
B& operator=(int rhs)
{
cout << "Assignment operator" << endl;
}
This would result in the first line: B ob = 5; to use the constructor, while the two following would use the assignment operator, see for yourself:
Constructor called
Assignment operator
Assignment operator
http://ideone.com/fAjoA4
If you do not want your constructor taking an int to be called upon assignment, you can declare it explicit like this:
explicit B(int x)
{
cout << "Constructor called" << endl;
}
This would cause a compiler error with your code, since it would no longer be allowed to implicitly construct an instance of B from an integer, instead it would have to be done explicitly, like this:
B ob(5);
On a side note, your constructor taking an int as parameter, is not a default constructor, a default constructor is a constructor which can be called with no arguments.
You are not taking the assignment operator into account. Since you have not defined your own operator=() implementation, the compiler generates a default operator=(const B&) implementation instead.
Thus, your code is effectively doing the following logic:
#include <iostream>
using namespace std;
class B
{
public:
B(int x) //custom constructor
{
cout << "Constructor called" << endl;
}
B(const B &b) //copy constructor
{
cout << "Copy constructor called" << endl;
}
B& operator=(const B &b) //default assignment operator
{
return *this;
}
};
int main()
{
B ob(5);
ob.operator=(B(6));
ob.operator=(B(7));
return 0;
}
The compiler-generated operator=() operator expects a B object as input, but you are passing an int value instead. Since B has a non-explicit constructor that accepts an int as input, the compiler is free to perform an implicit conversion from int to B using a temporary object.
That is why you are seeing your constructor invoked three times - the two assignments are creating temporary B objects.
This question already has answers here:
Copy constructor elision?
(2 answers)
Closed 8 years ago.
I am triying to see when each method is called in this example:
#include <iostream>
using namespace std;
class A {
public:
int x;
A(int x) : x(x) {cout<<"default ctor"<<endl;}
A(const A& a) : x(a.x) {cout<<"copy ctor"<<endl;}
A& operator =(const A& a) {cout<<"assignment op"<<endl;x=a.x;return *this;}
};
A f() { return A(5); }
int main() {
A a = f();
}
I expected the copy constructor to be called with the sentence return A(5) because as long as I know when an object is returned a temporary copy is created and returned. And also, in the sentence A a = f() I would expect the copy constructor to be called too because a is being initialized given another A object.
Why is default ctor being printed?
Two optimizations come into play here. Return Value Optimization (RVO)
And Copy Elision will merge f()'s return value directly into the destination variable via initialization. So this code:
A f() { return A(5); }
A a = f();
Optimizes to essentially:
A a(5);
Because c++ compilers are allowed to skip the operator= in cases of
A a = A();
A b = A(a);
and use constructors directly.
They are also allowed to perform Return Value Optimization for copy elision.
So in the end you have essentially A a(5);
This question already has answers here:
Function dual to std::move?
(3 answers)
Closed 9 years ago.
std::move can be used to explicitly allow move semantics when the move wouldn't be already allowed implicitly (such as often when returning a local object from a function).
Now, I was wondering (esp. in the context of local return and implicit move there), if there is such a thing as the inverse of std::movethat will prevent moving the object (but still allow copying).
Does this even make sense?
std::move converts an lvalue into an rvalue, and it does this essentially by static_cast. The closest to what I can think of as the opposite are these two type casts:
static_cast<T &>(/*rvalue-expression*/)
static_cast<const T&>(/*rvalue-expression*/)
An example of this can be seen below:
#include <iostream>
void f(const int &)
{ std::cout << "const-lval-ref" << std::endl; }
void f(int &&)
{ std::cout << "rval-ref" << std::endl; }
int main()
{
f(static_cast<const int &>(3));
return 0;
}
Casting the prvalue 3 to const int & ensures that the lvalue-overload of f is chosen.
In most contexts, you get this rvalue-to-lvalue modification automatically, simply by assigning to a variable:
int a = 3;
When you use a after this line, it will be an lvalue. This is true even when a is declared as rvalue reference:
int &&a = 3;
Here, too, a becomes an lvalue (basically because it "has a name").
The only situation where I can imagine an explicit cast to have any relevant effect is my first example above. And there, when dealing with prvalues like 3 or temporaries returned from function calls by copy, the only legal cast is to const-reference (a non-const-reference is not allowed to bind to a prvalue).
The solution to prevent an object to be moved is to make the move constructor of the object private, this way the object can't be moved but can be copied.
Example with move:
enter code here
#include <iostream>
class A {
public:
std::string s;
A() : s("test") {}
A(const A& o) : s(o.s) { std::cout << "move failed!\n";}
A(A&& o) : s(std::move(o.s)) { std::cout << "move was succesfull!\n"; }
};
int main(int argc, const char * argv[])
{
A firsObject;
A secondObject = std::move(firsObject);
return 0;
}
Example with move disabled:
#include <iostream>
class A {
public:
std::string s;
A() : s("test") {}
A(const A& o) : s(o.s) { std::cout << "move failed!\n";}
private:
A(A&& o) : s(std::move(o.s)) { std::cout << "move was succesfull!\n"; }
};
int main(int argc, const char * argv[])
{
A firsObject;
A secondObject = std::move(firsObject);
return 0;
}
template<class T>
T& unmove(T&& t)
{
return t;
}
This will change the value category of the argument expression to an lvalue, no matter what it was originally.
void f(const int&); // copy version
void f(int&&); // move version
int main()
{
int i = ...;
f(42); // calls move version
f(move(i)); // calls move version
f(i); // calls copy version
f(unmove(42)); // calls copy version
f(unmove(move(i))); // calls copy version
f(unmove(i)); // calls copy version
}