Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to elide copy when chaining?

Tags:

c++

c++17

c++14

I am creating a class of chaining-type, such as the small example below. It seems that when chaining member functions, then the copy constructor is invoked. Is there a way to get rid of the copy constructor call? In my toy example below, it is obvious that I'm only dealing with temporaries and thus there "should" (maybe not by the standards, but logically) be an elision. The second best choice, to copy elision, would be for the move constructor to be called, but this is not the case.

class test_class {
    private:
    int i = 5;
    public:
    test_class(int i) : i(i) {}
    test_class(const test_class& t) {
        i = t.i;
        std::cout << "Copy constructor"<< std::endl;
    }
    test_class(test_class&& t) {
        i = t.i;
        std::cout << "Move constructor"<< std::endl;
    }
    auto& increment(){
        i++;
        return *this;
    }
};
int main()
{
    //test_class a{7};
    //does not call copy constructor
    auto b = test_class{7};
    //calls copy constructor
    auto b2 = test_class{7}.increment();
    return 0;
}

Edit: Some clarifications. 1. This does not depend on optimization level. 2. In my real code, I have more complex (e.g. heap allocated) objects than ints

like image 727
DDaniel Avatar asked Nov 01 '19 10:11

DDaniel


People also ask

How does copy elision work?

Copy elision is the general process where, when returned from a function, an object is not copied nor moved, resulting in zero-copy pass-by-value semantics. It includes both return value optimization (RVO) and named return value optimization (NRVO).

What is copy elision in Javascript?

Copy elision is an optimization implemented by most compilers to prevent extra (potentially expensive) copies in certain situations. It makes returning by value or pass-by-value feasible in practice (restrictions apply).

What are copy constructors in C++?

Copy Constructor in C++ A copy constructor is a member function that initializes an object using another object of the same class. In simple terms, a constructor which creates an object by initializing it with an object of the same class, which has been created previously is known as a copy constructor.


1 Answers

  1. Partial answer (it doesn't construct b2 in place, but turns the copy construction into a move construction): You can overload the increment member function on the value category of the associated instance:

    auto& increment() & {
        i++;
        return *this;
    }
    
    auto&& increment() && {
        i++;
       return std::move(*this);
    }
    

    This causes

    auto b2 = test_class{7}.increment();
    

    to move-construct b2 because test_class{7} is a temporary, and the && overload of test_class::increment is called.

  2. For a true in-place construction (i.e. not even a move construction), you can turn all special and non-special member functions into constexpr versions. Then, you can do

    constexpr auto b2 = test_class{7}.increment();
    

    and you neither a move nor a copy construction to pay for. This is, obviously, possible for the simple test_class, but not for a more general scenario that doesn't allow for constexpr member functions.

like image 177
lubgr Avatar answered Oct 04 '22 15:10

lubgr