Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to enforce copy elision, why it won't work with deleted copy constructor?

Tags:

I have an uncopiable class. Copying this would be problematic. I want to guarantee that it won't be ever copied, so I made its copy constructor deleted:

class A {   public:     A();     A(const A&) = delete; };  A fun() {   return A(); };  int main() {   A a = fun(); }; 

Unfortunately, g++ won't compile this on the reason:

t.cc: In function ‘A fun()’: t.cc:8:12: error: use of deleted function ‘A::A(const A&)’    return A();             ^ t.cc:4:5: note: declared here      A(const A&) = delete;      ^ t.cc: In function ‘int main()’: t.cc:12:13: error: use of deleted function ‘A::A(const A&)’    A a = fun();              ^ t.cc:4:5: note: declared here      A(const A&) = delete;      ^ 

But this is a very clear situation where copy elision should be used, so the copy constructor shouldn't be ever called. Why is it so?

like image 477
peterh Avatar asked Jul 06 '16 13:07

peterh


People also ask

Why is my copy constructor not being called?

The reason the copy constructor is not called is because the copy constructor itself is a function with one parameter. You didn't call such function,so it didn't execute.

What is deleted copy constructor?

The copy constructor and copy-assignment operator are public but deleted. It is a compile-time error to define or call a deleted function. The intent is clear to anyone who understands =default and =delete . You don't have to understand the rules for automatic generation of special member functions.

Why we Cannot pass the argument by value to a copy constructor?

A copy constructor cannot receive value by call by value method. While we invoke copy constructor an object of same class must be passed to it. But the object passed need to be copied in formal arguments. Values of one object is copied in another by copy constructor.

Why does copy constructor accept the objects by reference and not by value?

It is necessary to pass object as reference and not by value because if you pass it by value its copy is constructed using the copy constructor. This means the copy constructor would call itself to make copy. This process will go on until the compiler runs out of memory.


2 Answers

Until C++17 copy elision is an optimization the compiler is not required to do, so classes must be copyable since the compiler might want to copy (even if it actually does not). In C++17 copy elision will be guaranteed in many cases and then classes won't need copy ctors.

See also:

http://en.cppreference.com/w/cpp/language/copy_elision

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html

https://herbsutter.com/2016/06/30/trip-report-summer-iso-c-standards-meeting-oulu/ (the bit about "Guaranteed copy elision")

You could perhaps use the old trick of declaring the copy constructor in your class but not actually implement it? That should please the compiler as long as it does not actually invoke the copy ctor. I didn't test that, but I believe it should work for your case until C++17 arrives.

like image 192
Jesper Juhl Avatar answered Sep 21 '22 02:09

Jesper Juhl


You can't force copy elision (yet) (see other answers).

However, you can provide a default move constructor for your class, this will move (and thus, not copy) the return value if RVO/NRVO is not possible. To do this you should add = default for your move constructors:

class A {   public:     A() = default;     A(const A&) = delete;     A(A&&) = default;     A& operator=(A&&) = default; }; 

Example

like image 30
Hatted Rooster Avatar answered Sep 23 '22 02:09

Hatted Rooster