I get a compile error with the following code:
main.cpp: In function âint main()â:
main.cpp:38: error: no matching function for call to âComplex::Complex(Complex)â
main.cpp:22: note: candidates are: Complex::Complex(Complex&)
main.cpp:15: note: Complex::Complex(double, double)
But when I change the argument type of the copy constructor to const Complex&, it works.
I was thinking that the default constructor will be called with 2 Complex::Complex(2.0, 0.0) and then the copy constructor will be called to create a with a copy of Complex(2.0. 0). Isn't it correct ?
#include <iostream>
using namespace std;
class Complex {
double re;
double im;
public:
Complex(double re=0, double im=0);
Complex(Complex& c);
~Complex() {};
void print();
};
Complex::Complex(double re, double im)
{
cout << "Constructor called with " << re << " " << im << endl;
this->re = re;
this->im = im;
}
Complex::Complex(Complex &c)
{
cout << "Copy constructor called " << endl;
re = c.re;
im = c.im;
}
void Complex::print()
{
cout << "real = " << re << endl;
cout << "imaginary = " << im << endl;
}
int main()
{
Complex a = 2;
a.print();
Complex b = a;
b.print();
}
When you write
Complex a = 2;
the compiler will not directly call the Complex constructor using 0 as default argument to build a but instead it will consider if it can "convert" 2 to a Complex.
To do the conversion it will find your Complex(re,im) version and could use that thanks to the default value and to the fact you didn't declare your constructor explicit, but then it will have to find a way transfer this value to a.
The tool for this "transfer" could be a copy constructor. However the complex value that can be built with Complex(re,im) is a temporary, and for some questionable reasons in C++ you are not allowed to pass a temporary as a non-const reference to a function.
So your copy constructor cannot be used with the temporary and the compiler is stuck as there are no ways to initialize a using 2.
If you declare your copy constructor instead accepting a const reference then the temporary can be passed to your copy constructor to initialize a and so everything works as you expect.
Directly initializing a could have been done using the syntax Complex a(2), that in this case doesn't need to use the copy constructor.
Note also that as strange it may be when you use the syntax Complex a = ... the compiler must check if it's legal to use a copy constructor, but once that legality has been checked it is allowed to not call it and use a direct initialization instead. In other words even if you need to declare your copy constructor accepting a const reference to be able to compile still the compiler may actually skip that part and directly build a without calling the copy constructor (even if the copy constructor - as in your case - has side effects). This apparently crazy rule has been added to be able to allow some optimizations in the generated code.
Copy constructors in C++ require the const part of the argument in order to take a const parameter, as you discovered. Otherwise you didn't create a copy constructor that can take a const argument, you created a copy constructor that takes a non-const Complex& as an argument.
You always create a copy construct with & so you don't have to copy the whole object for the function to use it. Creating an object copy takes time, referencing it is much more efficient.
In any case, it is required to preceed your object parameter with const so that the copy constructor is guaranteed not to change the input object. For instance:
Complex::Complex(const Complex &c)
{
cout << "Copy constructor called " << endl;
re = c.re;
im = c.im;
c.im = 'something'; // This would not work
}
Regards,
Dennis M.
The problem with this code is that your copy constructor is defined with this signature:
Complex::Complex(Complex &c)
This takes a non-const reference as a parameter, which probably isn't what you want. This would mean, for example, that if you try to copy a Complex object with the copy constructor, you'd be allowed to modify the original object!
To fix this, change your code to take the Complex by const reference:
Complex::Complex(const Complex &c)
More generally, copy constructors and assignment operators should always take in their arguments by const reference unless you have a very strong reason to think otherwise.
There are a few other things in your code I should probably point out. For starters, in this case, your copy constructor isn't necessary because it just does a straight copy of all the fields. There's a rule of thumb called the "rule of three" that says that you should only have a copy constructor if you have a destructor (and then you should also have an assignment operator). Otherwise, the default functions provided by the compiler should probably be sufficient for what you're doing.
Also, there's no reason to write your own Complex class, unless you absolutely must. The <complex> header defines complex<T> as a library class.
Related
I recently read about a std::unique_ptr as a #property in objective c and the suggestion to store a unique_ptr in ObjC as a property is as following:
-(void) setPtr:(std::unique_ptr<MyClass>)ptr {
_ptr = std::move(ptr);
}
My question is in ObjC, does the parameter get copied in this case? Because if that happens, unique_ptr shall never be declared as a property right?
My question is in ObjC, does the parameter get copied in this case?
That depends. Let me introduce a custom class where all copy operations are removed to better demonstrate possible outcomes under different circumstances:
struct MyClass {
MyClass() {
std::cout << "Default constructor" << std::endl;
}
MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;
MyClass(MyClass&&) {
std::cout << "Move Constructor" << std::endl;
}
MyClass& operator=(MyClass&&) {
std::cout << "Move Assignment" << std::endl;
return *this;
}
};
And change the parameter signature of your method accordingly:
- (void)setInst:(MyClass)inst {
_inst = std::move(inst);
}
Initialise the parameter with a temporary
Assuming the method in the sample belongs to a class named TDWObject the following code will compile just fine:
[[TDWObject new] setInst:MyClass{}];
Under C++17, you will find the default constructor and the move assignment called:
Default constructor
Move Assignment
The default constructor is called for the temporary, and thanks to guaranteed copy elision, neither copy nor move constructor is needed to initialise the parameter inst of the method with the temporary. The move assignemnt is straightforward - it happen when assigning result of std::move(inst) operation. If you use C++11 or C++14, standard doesn't guarantee copy elision, but clang will do it anyway. Some compilers use move-semantic instead, but overall for a temporary this code should work just fine.
Initialise the parameter with a moved named variable
Another option is to cast any named variable to an rvalue, and it will still allow to initialise the parameter without any issues:
MyClass inst;
[[TDWObject new] setInst:std::move(inst)];
The difference in this case, is that the function parameter will actually call the move constructor without elision optimisation:
Default constructor
Move Constructor
Move Assignment
Initialise the parameter with a named variable
And here is the broken scenario:
TDW::MyClass inst;
[self setInst:inst];
This will not work of course, because the parameter needs to call the copy constructor, which is marked deleted. The good thing about it, this code will never compile, and you will spot the problem straight away.
Considering alternatives
First of all I don't really think that Objective-C properties are compatible with non-copyable C++ classes and decided to give my own answer to the question linked, which you can review here.
I would not expect that code to compile as ptr is being passed by value there.
Better would be:
-(void) setPtr:(std::unique_ptr<MyClass>) &&ptr {
_ptr = std::move(ptr);
}
Edit: Thinking about it, that might not compile either. I don't know if Objective_C understands passing parameters by reference, rvalue or otherwise. But if it doesn't, this should work:
-(void) setPtr:(std::unique_ptr<MyClass>) *ptr {
_ptr = std::move(*ptr);
}
Consider the below code, where a composing class with another class as its member is being instantiated:
class CopyAble {
private:
int mem1;
public:
CopyAble(int n1) : mem1(n1) {
cout << "Inside the CopyAble constructor" << endl;
}
CopyAble(const CopyAble& obj) {
cout << "Inside the CopyAble copy constructor" << endl;
this->mem1 = obj.mem1;
return *this;
}
CopyAble& operator=(const CopyAble& obj) {
cout << "Inside the CopyAble assignment constructor" << endl;
this->mem1 = obj.mem1;
}
~CopyAble() {};
};
class CopyAbleComposer {
private:
CopyAble memObj;
public:
CopyAbleComposer(CopyAble m1) : memObj(m1) {
cout << "Composing the composer" << endl;
}
~CopyAbleComposer() {}
};
int main()
{
CopyAble ca(10);
CopyAbleComposer cac(ca);
return 0;
}
When I run this, I get the output:
Inside the CopyAble constructor
Inside the CopyAble copy constructor
Inside the CopyAble copy constructor
Composing the composer
Which means that the CopyAble copy constructor is being run twice - once when the CopyAble object is passed into the CopyAbleComposer constructor, and again when the initializer memObj(m1) runs.
Is this an idiomatic use of the copy constructor? It seems very inefficient that the copy constructor runs twice when we try to initialize a member object with a passed-in object of the same type, and it seems like a trap a lot of C++ programmers can easily fall into without realizing it.
EDIT: I don't think this is a duplicate of the question regarding passing a reference into the copy constructor. Here, we are being forced to pass a reference into a regular constructor to avoid duplicate object creation, my question was that is this generally known that class constructors in C++ should have objects passed in by reference to avoid this kind of duplicate copy?
You should accept CopyAble by reference at CopyAbleComposer(CopyAble m1), otherwise a copy constructor will be called to construct an argument. You should also mark it as explicit to avoid accidental invocations:
explicit CopyAbleComposer(const CopyAble & m1)
Pass-by-value and the associated copying is a pretty widely known property of C++. Actually, in the past C++ was criticized for this gratuitious copying, which happened silently, was hard to avoid and could lead to decreased performance. This is humorously mentioned e.g. here:
You accidentally create a dozen instances of yourself and shoot them all in the foot. Providing emergency medical assistance is impossible since you can't tell which are bitwise copies and which are just pointing at others and saying, "That's me, over there."
C++98
When any function/method is declared to receive an argument by value, this sort of copying happens. It doesn't matter if it's a constructor, a "stand-alone" function or a method. To avoid this, use a const reference:
CopyAbleComposer(const CopyAble& m1) : memObj(m1)
{
...
}
Note: even if you rearrange your code as below, one copy always remains. This has been a major deficiency in C++ for a long time.
CopyAbleComposer cac(CopyAble(10)); // initializing mem1 by a temporary object
C++11
C++11 introduced move semantics, which replaces the additional copy by a "move" operation, which is supposed to be more efficient than copy: in the common case where an object allocates memory dynamically, "move" only reassigns some pointers, while "copy" allocates and deallocates memory.
To benefit from optimization offered by move semantics, you should undo the "optimization" you maybe did for C++98, and pass arguments by value. In addition, when initializing the mem1 member, you should invoke the move constructor:
CopyAbleComposer(CopyAble m1) : memObj(std::move(m1)) {
cout << "Composing the composer" << endl;
}
Finally, you should implement the move constructor:
CopyAble(CopyAble&& obj) {
cout << "Inside the CopyAble move constructor" << endl;
this->mem1 = obj.mem1;
}
Then you should see that the "copy" message doesn't appear, and is replaced by the "move" message.
See this question for more details.
Note: In all these examples, the CopyAble objects are assumed to be much more complex, with copy and move constructors doing non-trivial work (typically, resource management). In modern C++, resource management is considered a separate concern, in the context of separation of concerns. That is, any class that needs a non-default copy or move constructor, should be as small as possible. This is also called the Rule of Zero.
The book I'm reading says that when your class contains a member that's a reference or a const, using the compiler-generated copy constructor or assignment operators won't work. For instance,
#include <iostream>
#include <string>
using namespace std;
class TextBlock
{
public:
TextBlock (string str) : s(str) {
cout << "Constructor is being called" << endl;
}
string& s;
};
int main () {
TextBlock p("foo");
TextBlock q(p);
q = p;
cout << "Q's s is " << q.s << endl;
return(0);
}
According to my book, both the lines TextBlock q(p); and q = p; should return compiler errors. But using the g++ compiler for Linux, I'm only getting an error for the line q = p; When I comment that out, this works fine and the code compiles. The correct s is output for Q, so it's apparently being copied by the compiler-generated copy constructor. I get the same results when I change the line string& s; to const string s.
Have there been some changes to C++ that now allow the copy constructor to be generated automatically for reference and const objects, but not the assignment operator? Or maybe I'm just not understanding the book correctly? Any thoughts?
The book is wrong. A const member or a reference member will
inhibit generation of the default copy assignment operator, but
doesn't prevent the compiler from generating a copy constructor.
Don't try to learn a special rule here.
The compiler-generated default versions of special member functions follow a simple pattern:
The type-appropriate special member function is called for every subobject (base classes and members).
From that, you can work out every case.
int i;
int &ri1 = i;
int &ri2 = ri1;
is allowed, so copying an object containing an int& is allowed.
There is no assignment operator for references (ri2 = ri1; does not rebind the reference), so assignment is not allowed.
References can't be default constructed:
int& ri; // error
so a type containing int& can't be default-constructed.
An important consideration is that the access checks are done for compiler-defaulted code just as if you had written it yourself. So interesting things can happen if a base class has, for example, a private copy constructor... and you don't have to learn special rules for any of them.
I do not understand how this code is compiling. Can somebody please explain what is going on in there.
#include <iostream>
using namespace std;
class B
{
public:
B(const char* str = "\0") //default constructor
{
cout << "Constructor called" << endl;
}
B(const B &b) //copy constructor
{
cout << "Copy constructor called" << endl;
}
};
int main()
{
B ob = "copy me"; //why no compilation error.
return 0;
}
The optput is:
Constructor called
P.S.: I could not think of a more apt title than this, Anyone who can think of a better title, please modify it.
The type of "copy me" is char const[8], which decays to char const *. Since the default constructor is not explicit, "copy me" can be implicitly converted to B, and thus ob can be copy-constructed from that implicitly converted, temporary B-object.
Had the default constructor been declared explicit, you would have had to write one of the following:
B ob1 = B("copy me");
B ob2("copy me");
Had the copy constructor also been declared explicit, you would have had to say one of these:
B ob3(B("copy me"));
B ob4("copy me");
In practice, all copies will be elided by any half-decent compiler, and you always end up with a single default constructor invocation.
Because this
B ob = "copy me";
invokes the copy constructor, which takes argument const B &b and you class B has a constructor
B(const char* str = "\0")
which is not defined as explicit.
The compiler is allowed to make one implicit conversion.
So, what happens here:
B ob = "copy me";
is:
Create a temp, unnamed object B, using the provided const char* str - this is allowed, since class B has constructor, which takes one argument and is not defined as explicit. In other words, all objects with type B can be constructed from a const char*
Create the object ob, using the temp object, created in 1..
If you add keyword explicit to your default constructor to prevent implicit conversion. Then it will not compile. Your answer is implicit conversion.
That's because of implicit conversion of the assignment statement to
B ob("copy me");
Try this to fail compilation (look at the explicit keyword):
#include <iostream>
using namespace std;
class B
{
public:
explicit B(const char* str = "\0") //default constructor
{
cout << "Constructor called" << endl;
}
B(const B &b) //copy constructor
{
cout << "Copy constructor called" << endl;
}
};
int main()
{
B ob = "copy me"; //why no compilation error.
return 0;
}
[it shouldn't compile because] the specified line does not match in data types
There is no compilation error because there exists a constructor of B that takes const char* as an argument, allowing for conversion between const char* and B.
This is because of implicit conversion. Add a Explicit to your Default constructor and try. it will Fail to compile.
Edit: after discussion with Kerrek SB I got a copy of the C++11 standard. I was wrong, there's a temporary. I'm editing this reply to reflect my newly acquired understanding.
15 years ago I knew C++ extremely well (that was around the time the first standard was to be released). I haven't used it since late 1998 so I have forgotten a lot and I know almost nothing of the recent standards.
The code is correct.
B ob = "copy me"; //why no compilation error.
There's no error. This is parsed as a declaration of an initialized variable. B is the type, ob the name of the variable, "copy me" the initializer.
The initializer is of type const char* and there is, in class B, a constructor that takes a const char* typed single argument. That constructor is not declared restricted by explicit and therefore can be used in this context.
Either a temporary object is created and then copied (you see both lines printed out) or the copy is elided and only the conversion constructor is called to construct directly at the destination (you see only one line printed). The standard explicitly allows this even if the copy constructor has side effects.
The program execution will print Constructor called to stdout. It may or may not then also print Copy constructor called. There is no error.
This question already has answers here:
Is there a difference between copy initialization and direct initialization?
(9 answers)
Closed 1 year ago.
Simple question: are the following statements equivalent? or is the second one doing more implicit things behind the scenes (if so, what?)
myClass x(3);
myClass x = myClass(3);
Thanks!
They are not completely identical. The first is called "direct initialization" while the second is called "copy initialization".
Now, the Standard makes up two rules. The first is for direct initialization and for copy initialization where the initializer is of the type of the initialized object. The second rule is for copy initialization in other cases.
So, from that point of view both are termed in one - the first - rule. In the case where you have copy initialization with the same type, the compiler is allowed to elide a copy, so it can construct the temporary you create directly into the initialized object. So you can end up very well with the same code generated. But the copy constructor, even if the copy is elided (optimized out), must still be available. I.e if you have a private copy constructor, that code is invalid if the code in which it appears has no access to it.
The second is called copy-initialization, because if the type of the initializer is of a different type, a temporary object is created in trying to implicitly convert the right side to the left side:
myclass c = 3;
The compiler creates a temporary object of the type of myclass then when there is a constructor that takes an int. Then it initializes the object with that temporary. Also in this case, the temporary created can be created directly in the initialized object. You can follow these steps by printing messages in constructors / destructors of your class and using the option -fno-elide-constructors for GCC. It does not try to elide copies then.
On a side-note, that code above has nothing to do with an assignment operator. In both cases, what happens is an initialization.
The second one may or may not call for an extra myclass object construction if copy elision is not implemented by your compiler. However, most constructors, have copy elision turned on by default even without any optimization switch.
Note initialization while construction never ever calls the assignment operator.
Always, keep in mind:
assignment: an already present object gets a new value
initialization: a new object gets a value at the moment it is born.
In the second one, a temporary object is created first and then is copied into the object x using myClass's copy constructor. Hence both are not the same.
I wrote the following to try and illustrate understand what's going on:
#include <iostream>
using namespace std;
class myClass
{
public:
myClass(int x)
{
this -> x = x;
cout << "int constructor called with value x = " << x << endl;
}
myClass(const myClass& mc)
{
cout << "copy constructor called with value = " << mc.x << endl;
x = mc.x;
}
myClass & operator = (const myClass & that)
{
cout << "assignment called" << endl;
if(this != &that)
{
x = that.x;
}
return *this;
}
private:
int x;
};
int main()
{
myClass x(3);
myClass y = myClass(3);
}
When I compile and run this code I get the following output:
$ ./a.out
int constructor called with value x = 3
int constructor called with value x = 3
This would seem to indicate that there is no difference between the two calls made in the main function, but that would be wrong. As litb pointed out, the copy constructor must be available for this code to work, even though it gets elided in this case. To prove that, just move the copy constructor in the code above to the private section of the class definition. You should see the following error:
$ g++ myClass.cpp
myClass.cpp: In function ‘int main()’:
myClass.cpp:27: error: ‘myClass::myClass(const myClass&)’ is private
myClass.cpp:37: error: within this context
Also note that the assignment operator is never called.