4

[C++11: 12.8/7]: If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor. Thus, for the class definition

struct X {
   X(const X&, int);
};

a copy constructor is implicitly-declared. If the user-declared constructor is later defined as

X::X(const X& x, int i =0) { /* ... */ }

then any use of X’s copy constructor is ill-formed because of the ambiguity; no diagnostic is required.

This doesn't make a lot of sense to me.

How does the code sample introduced by "thus" have anything to do with the "latter" case that I highlighted in italics?

My understanding so far is that:

  • If the class definition does not explicitly declare a copy constructor, and
  • the class definition does not declare a move constructor or move assignment operator, and
  • the class has a use-declared copy assignment operator or a user-declared destructor, then
  • the implicitly-defined copy constructor is defined as defaulted, and this is deprecated.

This seems odd in itself, that a standard-mandated behaviour is — in the same breath — deprecated. Does this mean that having a class that satisfies those three conditions is deprecated?

And what does the code sample have to do with it? That constructor is not a copy constructor, move constructor, move assignment operator, copy assignment operator or a user-declared destructor; it's just a user-declared constructor, no? Where's this "ambiguity"?

Could someone decode this passage for me?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • I'm more confused by a default parameter in a function definition. – Simon Richter Jan 13 '12 at 11:46
  • Yeah, it seems like an odd example in an already oddly-arranged paragraph. – Lightness Races in Orbit Jan 13 '12 at 11:47
  • @SimonRichter you can put them in either the declaration or the definition, but not both, obviously. Myself, I've never put it in the definition... – Liam M Jan 13 '12 at 11:49
  • @LiamM: Presumably there's a requirement a compile-time about the default parameter being visible to the calling scope. _[edit: 8.3.6 doesn't appear to say so, but (at least for member functions, the only example given of default arguments in **defining** declarations being valid, though this is not explicitly prohibited elsewhere) I can't see how else it would work.]_ – Lightness Races in Orbit Jan 13 '12 at 12:10

3 Answers3

4

Does this mean that having a class that satisfies those three conditions is deprecated?

Yes; if you have a use-declared copy-assignment operator or destructor, you should also declare a copy constructor. It's basically just saying that your class should follow the Rule of Three (or Five, or whatever it's called in C++11).

This seems odd in itself, that a standard-mandated behaviour is — in the same breath — deprecated.

The behaviour is mandated by the standard because that was the behaviour in C++03 - changing it would break legacy code. Just as the standard defines the behaviour of (for example) dynamic exception specifications, and also deprecates them.

How does the code sample introduced by "thus" have anything to do with the "latter" case that I highlighted in italics?

It doesn't have anything to do with the "latter" case.

It is an illustration of a potential problem caused by the normal behaviour defined by the clause: there will be a conflict between an implicitly-declared copy constructor, and a user-declared constructor with default arguments such that it can be called like a copy constructor.

Where's this "ambiguity"?

Consider the code:

X x1;  // assume there's also a default constructor
X x2(x);

Should x2 be initialised with the user-declared constructor (with the second argument taking its default value), or the implicitly-declared copy constructor? Both are an equally good match for the usage. That is the ambiguity.

Community
  • 1
  • 1
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
3

Just to answer the 2nd part. The ambiguity is due to the default parameter int i = 0, which allows that constructor to be called with only the first argument const X& x, which allows it to be used as a copy constructor as well because it is compatible with the signature X(const X& x) (§12.8/2 for the exact text). This conflicts with the implicit copy constructor, thus the ambiguity.

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
2

It seems to be clarifying the role of the move constructor and how it affects the copy constructor. It's saying that if you declare a move constructor or a move assignment operator, then there will be no implicit copy constructor. If you've defined your own copy constructor or copy assignment operator, you can disregard the bit you've placed in bold text.

I think the point of the example was to show that for the class:

struct X {
   X(const X&, int);
};

A copy constructor is implicitly defined, because there is no user defined copy constructor. However if you provide a default value for the second int argument (as they've done), then a call to:

X newX(oldX);

Could be a call to the user defined constructor or the copy constructor: This is the ambiguity they speak of.

Liam M
  • 5,306
  • 4
  • 39
  • 55