lmi
[Top][All Lists]
Advanced

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

Re: [lmi] Special-members strategy categories


From: Vadim Zeitlin
Subject: Re: [lmi] Special-members strategy categories
Date: Thu, 14 Jul 2022 14:22:38 +0200

On Thu, 14 Jul 2022 11:48:32 +0000 Greg Chicares <gchicares@sbcglobal.net> 
wrote:

GC> On 7/10/22 12:29, Vadim Zeitlin wrote:
GC> > 
GC> >  FWIW here is my personal strategy for defining the class' special
GC> > functions: there are several categories of classes
GC> > 
GC> > 1. Value-like: for them copy special functions and dtor are explicitly
GC> >    defaulted. Move special functions are omitted, and are not available.
GC> > 
GC> > 2. Container classes: they're almost value-like, but typically require
GC> >    defining move functions for efficiency reasons, so they're added,
GC> >    and possibly defaulted.
GC> > 
GC> > 3. Polymorphic classes: for them, copy special functions are explicitly
GC> >    deleted and a virtual dtor is defined and possibly defaulted. Move
GC> >    special functions are omitted, and are not available as the result.
GC> > 
GC> > 4. All the rest: for them, copy special functions are explicitly either
GC> >    default or deleted and the dtor is explicitly defined as well. Move
GC> >    special functions can be explicitly defined if needed.
GC> 
GC> Before giving deep thought to each one, let me ask whether any of these
GC> categories is actually empty for lmi. For example, wx has a large set
GC> of containers, but I think lmi has none, at least not in the STL sense.
GC> But lmi does have large set-of-named-data classes like LedgerVariant;
GC> would those fall into the Containers category because (I'm supposing)
GC> move functions might be helpful for them?

 Yes, for me it's a container-like class, even if it's not a container in
the STL sense.

GC> What about a class that contains, say, three vectors of a hundred
GC> elements each? If it has no move special functions, then aren't the
GC> vectors copied rather than moved? If so, would it be better to
GC> place classes that contain STL containers in the Containers category?
GC> and in that case, is the criterion really the size of the data the
GC> class contains (whether directly or, as with a std::vector member,
GC> indirectly)?

 Well, yes, if you wrap a vector into your own class, it's still a
container, isn't it? And if you wrap 3 vectors, surely it can only
strengthen rather than weaken the containericity characteristic of the
class.

 But, it's not _just_ the size of the data, it's also the fact that
LedgerVariant behaves like a container (AFAICS). If it were completely
semantically different from a container/value, it might not make sense to
define a move ctor for it even if it contained 100 vectors.

GC> Here again I perceive an emphasis on size: i.e., prefer to move
GC> large objects instead of copying them.

 AFAICS there are only 2 reasons to use move semantics in C++:

A. Arguably the more important one: allow passing around things that
   can't be copied, e.g. std::unique_ptr<>. In C++98, without move
   semantics, we had to use ugly hack in std::auto_ptr<> to allow this,
   while with move semantics we can simply allow moving while still
   disallowing copying.

B. For optimization purposes: move can be O(1), while copy is always O(N)
   where N is the object total logical size, i.e. not just its sizeof(),
   but the total size of its data, including data stored on the heap.

I think the case (A) is simple because you know perfectly well whether an
object is copyable or not and then it's just a matter of deciding whether
you ever want to pass it to/return it from the functions by value or not.
If you do, you have to define a move ctor to allow doing it.

 So, inevitably, all of the less clear cases are about (B), i.e. whether
it's worth moving something or not.

GC> Category (1) is small classes: don't move because it doesn't help.
GC> Category (2) is large classes: move because we're sure it'll help.
GC> 
GC> I don't see why category (3) shouldn't be moveable.

 You never use polymorphic values by value anyhow, so moving them is just
not useful.

GC> Because I don't feel certain whether most classes are in one of
GC> those three categories, I'd put most in all-other category (4),

 Including this category felt like a cop out (probably because it is one),
but I had to do it because there are clearly some classes that don't fit
neither of the 3 previous ones. But IME many of the classes do fit them and
for them the question about defining move operations or not is simple to
answer.

GC> for which the guidance is to enable copying or moving iff they
GC> make sense. I think I'd approach that question in two steps:
GC>  - If in C++98 a class would need to be copyable, then make it
GC>    copyable, or moveable, or both. (I ask this question first
GC>    because I think I already know how to answer it.)
GC>  - Then, unless we don't want to copy or move it, implement
GC>    copying if sensible, and also implement moving if the class
GC>    has a lot of data.

 This seems to miss a rather important case of classes that can't be copied
but still can be moved (std::unique_ptr<>, std::ifstream, ...). I'm not if
there are (m)any of those in lmi code, but IME they're quite common.

 In any case, I don't think we can come up with a concise rule giving the
answer in all cases without exception. There are general guidelines that
can cover most of them, but there are always going to be some exceptions.

 Regards,
VZ

Attachment: pgpmL3PfIu3jY.pgp
Description: PGP signature


reply via email to

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