29

I understand that when we define a class copy constructor of the class is necessary as Rule of three states. I also notice that the argument of the copy constructor is usually const as the following codes illustrate:

class ABC {
public:
    int a;
    int b;
    ABC(const ABC &other)
    { 
        a = other.a;
        b = other.b;
    }
}

My question is what would happen if the argument of the copy constructor is not const:

class ABC
{
    public:
    int a;
    int b;
    ABC(ABC &other)
    { 
        a = other.a;
        b = other.b;
    }
}

I understand that in some cases if the argument of the copy constructor is const then the second implementation would fail. Also if the argument of the copy constructor is const then the object to be copied will not change its content during the process. However, I do notice that some people still use the second implementation rather than the first one. Are there any reasons that the second implementation is preferred?

Saurav Sahu
  • 13,038
  • 6
  • 64
  • 79
feelfree
  • 11,175
  • 20
  • 96
  • 167
  • 4
    Why should `A` be modified in `ABC B(A)`? It makes little sense and would be quite non-intuitive behaviour. – juanchopanza Jun 06 '13 at 08:00
  • 10
    Probably because the author forgot to make it const. – Oliver Charlesworth Jun 06 '13 at 08:00
  • If we go by your title, this is a duplicate: [Why is the copy constructor argument const](http://stackoverflow.com/questions/1602058/why-is-the-copy-constructor-argument-const) (If we go by the end of your text, it's not) – jogojapan Jun 06 '13 at 08:03
  • 3
    It might also be that some people try to pull tricks a la `auto_ptr`. Of course, given that even the standards committee couldn't get that right, it is a *very* bad idea to do so. – celtschk Jun 06 '13 at 08:03
  • It could be so that the compiler can decide to use the copy constructor or not in various optimisations without changing the meaning of the program. If the state of a copied object was changed, RVO for example may change the behavior of the program. – Joachim Isaksson Jun 06 '13 at 08:06
  • 1
    I can imagine that the copy constructor needs to use methods of &other , that are not declared as `const` themselves. Probably and hopefully in a way which does not change &other. – flaschenpost Jun 06 '13 at 08:06
  • 1
    @flaschenpost: Then those methods should be declared `const`. – celtschk Jun 06 '13 at 08:08
  • @celtschk: if I had a nickel for every method/argument that should have been marked `const` and that was not, I would no longer need to work ;) And unfortunately, you may not be in a position to change the code... – Matthieu M. Jun 06 '13 at 09:33
  • *"I understand that when we define a class copy constructor of the class is necessary as Rule of three states."* - Well, most of the time it's actually *not neccessary* (at least not from you) due to the [*Rule of Zero*](http://flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html), as in your example. – Christian Rau Jun 06 '13 at 10:35
  • @MatthieuM.: Of course, there's still `const_cast` in order to keep the decease quarantined. So it's still no excuse to make the constructor argument non-const. – celtschk Jun 06 '13 at 18:52

8 Answers8

41
  • Logically, it should make no sense to modify an object of which you just want to make a copy, though sometimes it may have some sense, like a situation where you'd like to store the number of time this object has been copied. But this could work with a mutable member variable that stores this information, and can be modified even for a const object (and the second point will justify this approach)

  • You would like to be able to create copy of const objects. But if you're not passing your argument with a const qualifier, then you can't create copies of const objects...

  • You couldn't create copies from temporary reference, because temporary objects are rvalue, and can't be bound to reference to non-const. For a more detailed explanation, I suggest Herb Sutter's article on the matter

JBL
  • 12,588
  • 4
  • 53
  • 84
  • 7
    There is also the fact that the version taking non-const references will not work for temporaries. – juanchopanza Jun 06 '13 at 08:10
  • just a note: even reference counting smartpointers don't need mutables. The reference counter isn't located in the smartpointer itself but usually in a special intermediate object or inside the referent (intrusive implementations). So original _smartpointer_ remains unmodified and may be declared const. – user396672 Jun 06 '13 at 09:14
  • @user396672 I'll correct to remove the misleading example then – JBL Jun 06 '13 at 09:26
14

The last thing any consumer of your class would expect is a copy constructor that changed the object that was copied! Therefore, you should always mark as const.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
12

There are two reasons that const may be needed here:

  1. It ensures that you don't accidentally "damage" the original when making the copy - this is a good thing, because you don't really want your original object to be changed when making a copy of it!
  2. You can pass in something other than a basic object - since the constructor takes a reference, if it's not an object itself - say for example an expression.

To exemplify the second case:

 class ABC
    {
       public:
           int a;
           int b;
       ABC(const ABC &other)
       { 
         a = other.a;
         b = other.b;
       }
       ABC operator+(const ABC &other)
       {
           ABC res;
           res.a = a + other.a;
           res.b = b + other.b;
           return res;
       }
    }

  ...
  ABC A;
  a.a = 1;
  a.b = 2;
  ABC B(a+a);

This won't compile if the constructor is ABC(ABC &other), since a+a is a temporary object of type ABC. But if it's ABC(const ABC &other), we can use the temporary result of a calculation and still pass it in as a reference.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
6

As several other answers point out, a copy constructor that modified its argument would be an unpleasant surprise. That is not, however, the only problem. Copy constructors are sometimes used with arguments that are temporaries. (Example: return from function.) And non-const references to temporaries don't fly, as explained elsewhere on SO.

Community
  • 1
  • 1
Andrew Lazarus
  • 18,205
  • 3
  • 35
  • 53
3

If the copy constructor doesn't specify it's parameter as const then this fragment would not compile.

const ABC foo;
ABC bar(foo);
Paul Mitchell
  • 3,241
  • 1
  • 19
  • 22
1

Copy constructors should not modify the object it is copying from which is why the const is preferred on the other parameter. Both will work, but the const is preferred because it clearly states that the object passed in should not be modified by the function.

const is for the user only. It doesn't exist for the actual executable.

Serdalis
  • 10,296
  • 2
  • 38
  • 58
  • 2
    Not entirely true; if the object does e.g. reference counting then the rhs may need to be modified. – Oliver Charlesworth Jun 06 '13 at 08:03
  • 4
    @OliCharlesworth: I would argue that should be a `mutable` member, since usually the reference count is not part of the observable state – Andy Prowl Jun 06 '13 at 08:04
  • @AndyProwl: agreed. I just wanted to dispel the assertion that the rhs is never modified ;) – Oliver Charlesworth Jun 06 '13 at 08:05
  • 1
    @OliCharlesworth: If the object is using reference counting then the reference count cannot be part (i.e. a direct or indirect sub-object) of the source object so the rhs still shouldn't be modified (although things that it "part owns" or points to can be modified) and should still be const. – CB Bailey Jun 06 '13 at 08:06
  • @AndyProwl: Absolutely not! If the reference count is mutable it's still a sub-object of the source object and would be destroyed when the source object's lifetime ends. This is bad for the copy which now (presumably) has a pointer to a reference count that no longer exists. – CB Bailey Jun 06 '13 at 08:08
  • @CharlesBailey even if `static`? – Serdalis Jun 06 '13 at 08:09
  • @CharlesBailey: Right, I didn't have a clear picture in my mind. – Andy Prowl Jun 06 '13 at 08:13
  • @CharlesBailey: The RHS may involve inheritance where the base obj has something you want to modify but the derived object you want to remain const. An example might be a BufferPool type object that has a fixed number of buffers you reserve and this inherits from a base class that deals with critical sections (mutex) to serialize this access. You need to pass a const BufferPool & but in your new object, you need to try to acquire the mutex of the base class. Example where this comes up for me is using scope locks on method calls and letting the dtor invokes base class behaviors (unlock) – Eric Jul 27 '16 at 16:40
1

In addition to the fundamental assumption that copy constructors should not modify the source instance, this article elaborates on the actual technical reason for using const:

http://www.geeksforgeeks.org/copy-constructor-argument-const/

Namely that and I quote:

"... compiler created temporary objects cannot be bound to non-const references ..."

infinite-loop
  • 882
  • 11
  • 17
0

It's not a "must" in technical sense. We even have such beast right in the standard, though it got deprecated. Follow the links for the reasoning.

The semantics of copy we expect is to leave the "template" unchanged and provide a clone that is an exact equivalent in all regards, that you have hard time to tell form original.

That being expected you shall think twice to have a copy ctor that does otherwise. It will surprise users and likely introduce bugs. And frustration and noise, just try to google for 'vector of auto_ptr' just to see the count.

The remainder of the question could be "I swear not to touch the original in implementation, but want a different signature". What signature then? Let's try T and T&.

T drops out as it would require copy ctor to be usable and we're implementing just that. Recursion: see recursion.

That leaves T&. That would actually work for a deal of cases. But just fail if your original object happens to sit around in a const form, or be temporary. Why hinder that sensible case for no rain at all?

Community
  • 1
  • 1
Balog Pal
  • 16,195
  • 2
  • 23
  • 37