C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Should copying of the same type or its subtype be prohibited before constructing an object?

From: Jason McKesson <jmckesson_at_[hidden]>
Date: Mon, 13 May 2024 11:33:39 -0400
On Mon, May 13, 2024 at 11:09 AM David wang via Std-Proposals
<std-proposals_at_[hidden]> wrote:
>
> Yes, delegating constructors should not be allowed to pass-by-value. It is one of the cases of the rule.

But that isn't even a delegating constructor. At least, not in terms
of the C++ meaning of that term, as defined in [class.base.init]/6.

If you mean it in the vernacular sense of "it calls some other
constructor", OK, but your initial post seems invalid. This isn't a
performance optimization; you're just asking for the thing you asked
for earlier in a different thread. So I don't know why you tried to
pass off the same request as a performance optimization.

In any case, you need to address a very important point.

What you want introduces an irregularity in the language. At the
present time, what this code means and does is clear and unambiguous.
Furthermore, it does the same thing it would do in any other case. If
you do this:

```
some_type t;
base u(t);
```

This will do the same thing, regardless of where it exists.

What you want to do is change the language to make it irregular. Which
constructor will be called now will depend on the context of that
constructor call's location. In some places, it will call the template
constructor. In other places it won't.

Irregularities are... bad, actually. Making code that looks the same
but behaves differently based on where that code is located is not a
good thing. It may sometimes be necessary or extremely useful. But you
need to actually make that argument. You need to explain why the cost
of irregularity is worth it.

In particular, is this an idiom that people actually use? Is this a
common confluence of code that people see a lot? Because on the face
of it, the special case seems so rare that I don't buy that it needs
to be addressed. In order to encounter this problem, the following set
of things needs to happen:

1. A user writes a class intended to be used as a base class (note: it
is a code smell to use a class as a base class if that class isn't
intended for that purpose. It's not always wrong, but it should be
looked at as dubious).

2. The base class has a template constructor that takes exactly one
parameter by value (which suggests that this constructor has a meaning
that is very distinct from copying it. That is, it is not intended to
take itself as a parameter).

3. A user writes a class derived from this class. Seemingly unaware of
the base class's interface, the user attempts to invoke the base
class's copy constructor by passing itself as a parameter to the base
class constructor.

Where exactly things went wrong here is debatable. I would argue that
#1 and #2 are inconsistent with each other. If you're making a class
that is intended to be a base class, you probably shouldn't create a
template constructor that would interfere with initializing that base
class. Note that types like `std::any`, despite not being intended to
be a base class, don't take their template parameter by value. But an
argument could be made that step 3 also is a bug, as the user should
have looked at the class's constructors before trying to use them.

Regardless, the key question to me is this: how often do these
circumstances *actually* happen in the real world? Do they happen
often enough to warrant creating inconsistency in the behavior of the
language? Is there some important C++ idiom that's being blocked by
this?

This has to happen often enough to pay the costs of making the
language inconsistent.

Received on 2024-05-13 15:33:52