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
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).
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).
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With