c++copy-constructorrvo

Reduce Copy Constructor Calls


The following code is a minimal example from a project that I'm working on. The main question is that I want to cut down the number of calls to the copy constructor, but it is not clear to me the right way to do this.

#include<iostream>

class MyClass
{
public:
    MyClass() {std::cout << "Default Constructor\n";}
    MyClass(const MyClass &input) {std::cout << "Copy Constructor\n";}
    MyClass & operator=(const MyClass &input) 
         {std::cout << "Assignment\n"; return *this;}
    MyClass & operator+=(const MyClass &input) {return *this;}
    friend MyClass operator+(MyClass lhs,const MyClass &);
};

MyClass operator+(MyClass lhs,const MyClass &rhs) 
    {lhs+=rhs;return lhs;}

int main()
{
    MyClass a,b,c;
    c=a+b;
    return 0;
}

When I run the code, the output is:

Default Constructor
Default Constructor
Default Constructor
Copy Constructor
Copy Constructor
Assignment

Main Question: In my application, the copy constructor is expensive (it involves memory allocation). Assignment, on the other hand, is relatively cheap. What is the proper way to have fewer calls to the copy constructor?

I've considered a few solutions, but none make me happy:

Copy constructor called twice, Copy constructor called twice, and Conditions for copy elision

Responses to comments:


Solution

  • In other to minimize copy constructor calls, I recommend you define the move constructor and that you perfect-forward your operator arguments. Move constructor:

    MyClass(MyClass &&input) {std::cout << "Move Constructor\n";}
    

    Perfect-forwarding operator:

    template<typename T>
        friend MyClass operator+(T &&lhs,T &&rhs) {return std::forward<T>(lhs);}
    

    With the right calls, your operator will involve a move constructor instead of a copy constructor. For instance, if you add objects that come out of a function and store the result immediately (e.g. MyClass c=a+b; instead of MyClass c;c=a+b;), thanks to RVO you can save the copy constructor.

    Let's say you have a function that returns a MyClass instance:

    MyClass something() {return MyClass();}
    

    If you add the function return values and store them immediately, e.g.:

    MyClass c=something()+something();
    

    Then no copy constructor will be ever involved.

    I have put a series of examples here where I used the const MyClass& parameter with operator+ and perfect-forwarding parameters with operator-. You can see that it makes a difference in the last example but not in all the other ones. That's why I said "with the right calls". If you have to manipulate objects that can be forwarded like that, it might be worth a shot.