How to force the compiler to use an explicit copy constructor?

I wrote a small test program with a sample class that also contains a self-defined constructor, destructor, copy constructor, and assignment operator. I was surprised when I realized that the copy constructor was not called at all, although I implemented functions with return values ​​of my class and strings of type Object o1; Object o2(o1); Object o1; Object o2(o1);

innerclass.hpp:

 #include <iostream> class OuterClass { public: OuterClass() { std::cout << "OuterClass Constructor" << std::endl; } ~OuterClass() { std::cout << "OuterClass Destructor" << std::endl; } OuterClass(const OuterClass & rhs) { std::cout << "OuterClass Copy" << std::endl; } OuterClass & operator=(const OuterClass & rhs) { std::cout << "OuterClass Assignment" << std::endl; } class InnerClass { public: InnerClass() : m_int(0) { std::cout << "InnerClass Constructor" << std::endl; } InnerClass(const InnerClass & rhs) : m_int(rhs.m_int) { std::cout << "InnerClass Copy" << std::endl; } InnerClass & operator=(const InnerClass & rhs) { std::cout << "InnerClass Assignment" << std::endl; m_int = rhs.m_int; return *this; } ~InnerClass() { std::cout << "InnerClass Destructor" << std::endl; } void sayHello() { std::cout << "Hello!" << std::endl; } private: int m_int; }; InnerClass innerClass() { InnerClass ic; std::cout << "innerClass() method" << std::endl; return ic; } }; 

innerclass.cpp:

 #include "innerclass.hpp" int main(void) { std::cout << std::endl << "1st try:" << std::endl; OuterClass oc; OuterClass oc2(oc); oc.innerClass().sayHello(); std::cout << std::endl << "2nd try:" << std::endl; OuterClass::InnerClass ic(oc.innerClass()); ic = oc.innerClass(); } 

Output:

  1st try: OuterClass Constructor OuterClass Copy InnerClass Constructor innerClass() method Hello! InnerClass Destructor 2nd try: InnerClass Constructor innerClass() method InnerClass Constructor innerClass() method InnerClass Assignment InnerClass Destructor InnerClass Destructor OuterClass Destructor OuterClass Destructor 

After some research, I read that there is no guarantee that the compiler will use an explicitly defined copy constructor. I do not understand this behavior. Why does the copy constructor even exist if we don’t know what it is called? How does the compiler decide if it uses it?

Or, even better, is there a way to force the compiler to use a self-defined copy constructor?

+4
source share
6 answers

For completeness only, with other answers, the standard allows the compiler to omit the copy constructor in certain situations (other answers are called "Return Value Optimization" or "Lowest Return Value Optimization" - RVO / NRVO):

12.8 Copying class objects, clause 15 (C ++ 98)

Whenever a temporary class object is copied using the copy constructor, and this object and copy are of the same cv-unqualified type, implementations are allowed to process the original and copy as two different ways of accessing the same object and not copy at all, even if the constructor or destructor of the class copy has side effects. For a function with a return type of the class, if the expression in the return statement is the name of the local object, and the cv-unqualified type of the local object matches the type of the return function, implementations are allowed to omit the creation of a temporary object to store the return value of the function, even if the constructor or destructor of the class instance has side effects. In these cases, the object is destroyed at later times when the original and the copy would have been destroyed without optimization.

So, in your innerClass() method, the copy constructor, which, in your opinion, is called upon return, is allowed to be optimized:

 InnerClass innerClass() { InnerClass ic; std::cout << "innerClass() method" << std::endl; return ic; // this might not call copy ctor } 
+6
source

You should not create your class using a copy constructor called (or not called) under certain circumstances. The compiler is allowed to delete or add calls to the call constructor in various places, and you really do not want to track them.

As for why you need to - well, the compiler can decide that it needs a copy, and the copy constructor is what it uses to do this. The advantage of copy constructor calls that are resolved is performance — copying is usually quite expensive.

+4
source

I agree with Neil, you should not write a class that depends on the called constructor of the copy. Namely, because the compiler can do things like “Named Return Value Optimization” ( link ), which completely avoids the copy constructor in many return value scripts. To force a copy constructor to be called, you need to write a lot of code designed to “cheat” the C ++ compiler. Not a good idea.

In a specific scenario, though, if you want to force a call to the copy constructor, you can make an explicit call.

 Object SomeFunc() { Object o1 = ... return Object(o1); } 
+4
source

This is problem?

 OuterClass(const OuterClass & rhs) { std::cout << "OuterClass Constructor" << std::endl; ==> std::cout << "OuterClass Copy Constructor" << std::endl; } OuterClass & operator=(const OuterClass & rhs) { std::cout << "OuterClass Constructor" << std::endl; ==> std::cout << "OuterClass Assignment operator" << std::endl; } 

Error copying folders!

I would suggest you debug the code once to find out what exactly is happening. Debugging really helps you find problems.

EDIT: for an internal class problem:

As already noted, this applies to Name Return Value Optimization (NRVO).

 InnerClass innerClass() { InnerClass ic; std::cout << "innerClass() method" << std::endl; return ic; } 

the compiler can convert the function to

 void innerClass( InnerClass &namedResult) { std::cout << "innerClass() method" << std::endl; } 

Thus, it eliminates both the return of the value of the class object and the need to call the copy constructor of the class.

Follow the two links below to learn more about NRVO:

+2
source

Object o1(); does not create any objects, but defines a function prototype with the function name o1, void arguments and the return type as Object. You need to publish the code to find the actual problem.

0
source

Put some code. I think you are using the wrong syntax.

The copy constructor must definitely have the following signature:

 MyObject(const MyObject&) 

Then you can see if the copy constructor is called with the following code:

 MyObject m1; MyObject m2(m1); 

You are not allowed to use MyObject m1(); which is a function declaration.

0
source

All Articles