lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] Rule of zero


From: Vadim Zeitlin
Subject: Re: [lmi] Rule of zero
Date: Tue, 20 Apr 2021 17:20:00 +0200

On Tue, 20 Apr 2021 13:19:25 +0000 Greg Chicares <gchicares@sbcglobal.net> 
wrote:

GC> On 4/20/21 12:30 PM, Vadim Zeitlin wrote:
GC> [...]
GC> >  Concerning the pursuit of the rule of zero, I have to say that I disagree
GC> > with it and believe that it's always best to write all the ctors
GC> > explicitly, even if just to default them, for all but the most trivial
GC> > classes. I thinks understand why you do what you do, but your approach
GC> > seems to implicitly rely on the infallibility of the programmer writing 
the
GC> 
GC> We're mathematicians. Striving for infallibility is our job.

 Mathematicians traditionally didn't have the possibility of automatically
checking their proofs and while it's a very interesting question of what
does it change to have this possibility now, I don't think we need to
discuss this because the situation is just not the same for us, as there is
no doubt in my mind that we do want to use all the automatic assistance we
can get.

GC> On days when we feel less than fully infallible, we write
GC> assertions. I was hoping that statically asserting that the
GC> four special members for copying and moving are 'trivial'
GC> would guarantee that a class is free of a whole category
GC> of defects. I haven't proven to myself that that won't work,
GC> so I'm still hoping that it might.

 The question of defaulting or omitting the ctors doesn't seem to be
related to them being trivial or not. I.e. of the 4 possibilities {ctor
is/is not trivial} X {ctor is/is not explicitly defined} only one one
("explicitly defined and trivial") is impossible, but the other 3 are.
Why do you think it's useful to speak about the ctor triviality in the
context of this discussion? I just don't see how could it possibly help.
And I also strongly suspect that there are very few classes with trivial
ctors in lmi anyhow.

 IOW:

(a) Having trivial ctor doesn't guarantee the absence of defects. If
    anything, it's a fertile ground for having them.

(b) The vast majority of lmi classes don't have trivial ctor anyhow, so
    anything based on checking for this is irrelevant for them.


 I think you're well aware of the difference between having a trivial and
user-defined (and not just user-declared, which includes defaulted) ctor,
but it seems to me that sometimes you might be thinking about the latter
when you speak about the former. I.e. I would understand your statement
above if it were formulated in terms of user-defined ctors, could this, by
chance, be what you meant? Of course, in practice this wouldn't help much
because, AFAIK, we can't test if a class has a user-defined ctor anyhow.


GC> > code, as it assumes that if a ctor is not defined, it is because it is not
GC> > needed and not just because it was forgotten. Personally I'd rather avoid
GC> > assuming this and explicitly show that it wasn't forgotten, but was
GC> > intentionally omitted by writing the default version
GC> 
GC> The default ctor is a unique kind of special member function,
GC> for which memcpy-ability is not the definition of triviality.

 Sorry, I still completely disagree with this. memcpy() is obviously
specific to copying, i.e. makes sense only in the context of the copy ctor.
There is nothing unique or special about the default ctor not using it. If
you meant memset(), rather than memcpy(), then I propose to make a thought
experiment in which trivial default ctor would use memset(0) to initialize
the object memory -- and verify that this wouldn't change absolutely
anything in the context of this discussion. I.e., again, the fact that
trivial default ctor doesn't do anything is simply irrelevant.

GC> But if I write an explicit ctor and no default ctor, and the
GC> code compiles, I don't see why I should fret over whether it
GC> was deliberately omitted or just forgotten.

 I don't know why should you do it and whether you should do it at all, but
I can explain why do I do it and that's because I've seen default (or, more
typically, copy) ctor forgotten, rather than intentionally not implemented,
many, many times in the past, including in my own code. So it has become a
reflex to check whether it's indeed correct not to have it whenever I see a
class without it because I know that often it is a mistake.

 This reflex might be out of date with C++11 member initializers because as
long as all members do have them, chances are that the default ctor is not
needed. But it still applies in the copy ctor as much as ever, I believe,
and it would seem strange to make an exception for the default ctor only
but keep explicitly defaulting the copy ctor.

GC> If I need it, but don't have it, the compiler will tell me;

 Sorry, but no, absolutely not. A trivial example is not initializing the
enum element in your example earlier in this thread -- it means that using
it is undefined behaviour, yet the compiler wouldn't tell you anything
except in the most trivial cases.

GC> if the compiler doesn't tell me I need it, then I don't.

 No, the compilers are not nearly smart enough for this.

GC> > in C++98 I always
GC> > used a stock comment to indicate that it was the case
GC> 
GC> Exactly:
GC> 
GC> $ git grep 'Implicitly-declared special member functions do the right 
thing.' |wc -l
GC> 48

 So what exactly has changed for you to not consider replacing this comment
with the equivalent statement at the code level in C++11? It seems to me
that the same rationale that was used for having these comments can be used
to explain why we need to explicitly default the ctors in C++11.

GC> > but since C++11 writing "= default" serves the same purpose while
GC> > being also enforced by the compiler, which is much nicer. The gain
GC> > from omitting these lines just doesn't seem to be commensurate with
GC> > the peace of mind gained from having them.
GC> 
GC> There's value in omitting them: they're clutter; and we've had
GC> cases where a defaulted dtor has to be moved out of a header
GC> so that unique_ptr can find its deleter.

 I'm very confused by this counter example because not having a ctor at all
absolutely wouldn't help here. I.e. instead of moving the existing
defaulted ctor from the header to the source file, you'd have to add the
defaulted ctor after adding a unique_ptr member. How would this have been
any better?

GC> And AIUI defaulting the copy operations prevents the compiler from
GC> providing the move operations for us

 I'm even more certain that any move ctor should be explicitly declared
anyhow. Relying on implicit move, with its destructive semantics, just
seems too dangerous.


GC> That's why I'm experimenting with a new idea:
GC>  - in almost all classes, never write code that would prevent
GC>    the compiler from generating correct (and non-deleted)
GC>    copy and move functions; and
GC>  - try to find a way to assert this property, so that the
GC>    stock comment above could be replaced by a stock assertion;
GC>  - and then there need be no "=default" clutter.

 Even if the second point could be implemented, and I don't see how could
it be right now (and I'm almost certain it can't be done in any kind of
reasonably simple and readable way), I can't imagine why would having
static asserts be better than having explicitly defaulted ctors.

 I'm really sorry for being so obstinate, but I'm pretty sure that we have
a simple, working and obviously correct solution to the problem which
consists in explicitly writing defaulted special member functions and I
have a lot of trouble understanding why exactly do you want to do something
different, which can only be more complicated and less obvious (even
assuming it can be correct, which is something I'm not sure at all about).

 IMO the choices are:

1. Explicitly default all the ctors that should be defaulted.
2. Don't do it but write a comment that this is fine, as in C++98.
3. Don't do it and don't write any comments.

 And for me 1 > 2 >> 3. The possible choice

4. Don't do it and write asserts checking that ctors are not needed.

would have to be implemented first in order to be evaluated, but it will
probably fall in somewhere between 1 and 2, i.e. I simply don't see how
could it result in anything better than 1 as it will definitely result in
more clutter than it.

 Please let me know if you think I'm missing something here,
VZ

Attachment: pgpDY6BVXUmWR.pgp
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]