lmi
[Top][All Lists]
Advanced

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

Re: [lmi] Detecting whether move semantics actually take place


From: Vadim Zeitlin
Subject: Re: [lmi] Detecting whether move semantics actually take place
Date: Thu, 28 Jul 2022 18:40:42 +0200

On Thu, 28 Jul 2022 14:08:27 +0000 Greg Chicares <gchicares@sbcglobal.net> 
wrote:

GC> On 7/23/22 15:41, Vadim Zeitlin wrote:
[...]

 I think I understand what you want to achieve better now, thanks for the
explanations.

GC> > GC> Suppose lmi copies an object, and I want to consider moving it 
instead.
GC> > GC> I can write the move operation, but will move semantics actually take
GC> > GC> place, or will the compiler silently substitute a copy to fulfill my
GC> > GC> move request? I don't simply want to make the request; I want to know
GC> > GC> how it's fulfilled. If moving is inherently impossible for some class,
GC> > GC> I'd like to know that before I waste time trying to move it.
GC> > 
GC> >  I think the simplest way of answering this question is to declare the 
move
GC> > constructor as default and rely on clang -Wdefaulted-function-deleted to
GC> > tell you if it turns out that your move ctor is implicitly deleted. Isn't
GC> > it?
GC> 
GC> Suppose the chronology is:
GC>  1. I write a class that's effectively-movable, but don't actually move it 
yet.
GC>  2. I change the class in a way that spoils its movability.
GC>  3. A decade later, I try to move it, and find that I can't.
GC> Doesn't clang give that warning only at the third step, where I want it
GC> at the second?

 No, it does give it at step (2) if it gives it at all. The warning
(-Wdefaulted-function-deleted) is given as soon as the class is declared,
there is no need to use it at all. E.g. here is a stupid but self-contained
example:
---------------------------------- >8 --------------------------------------
% cat -n non_movable.cpp
     1  struct non_movable {
     2      non_movable() = default;
     3      non_movable(non_movable const&) = default;
     4      non_movable(non_movable&&) = default;
     5      non_movable& operator=(non_movable const&) = default;
     6      non_movable& operator=(non_movable&&) = default;
     7
     8      int const x = 17;
     9  };
% clang++ -fsyntax-only -Wall -std=c++20 non_movable.cpp
non_movable.cpp:5:18: warning: explicitly defaulted copy assignment operator is 
implicitly deleted [-Wdefaulted-function-deleted]
    non_movable& operator=(non_movable const&) = default;
                 ^
non_movable.cpp:8:15: note: copy assignment operator of 'non_movable' is 
implicitly deleted because field 'x' is of const-qualified type 'const int'
    int const x = 17;
              ^
non_movable.cpp:6:18: warning: explicitly defaulted move assignment operator is 
implicitly deleted [-Wdefaulted-function-deleted]
    non_movable& operator=(non_movable&&) = default;
                 ^
non_movable.cpp:8:15: note: move assignment operator of 'non_movable' is 
implicitly deleted because field 'x' is of const-qualified type 'const int'
    int const x = 17;
              ^
2 warnings generated.
---------------------------------- >8 --------------------------------------

GC> > GC> Similarly,
GC> > GC> I'd like to know whether moving is only incidentally impossible: e.g.,
GC> > GC>  - because I've made the move functions inaccessible; or
GC> > GC>  - because I've deleted them for no good reason, just because I didn't
GC> > GC>      want to spend time considering them; or
GC> > GC>  - because I made a base class unmoveable for some such reason; or
GC> > GC>  - because I casually added a 'const&' data member for no strong
GC> > GC>      reason, rendering the class unassignable;
GC> > GC> because then I might want to consider removing that incidental cause.
GC> 
GC> I'm confused here:
GC> 
GC> >  Note that is_effectively_moveable<T> doesn't actually help answering 
these
GC> 
GC> It doesn't help?

 Sorry for being unclear, but I was answering specifically the "I'd like to
know whether moving is only incidental impossible" part here. I.e. the idea
was that while static_assert(is_effectively_moveable) does tell you if
moving is possible or not, it doesn't explain _why_ is it impossible if it
isn't.

GC> > questions. But both the clang warning and is_effectively_moveable concept
GC> > (if you defined it, see below) do
GC> 
GC> Yet it does help?

 Unlike the static_assert, the warning shows why exactly the class can't be
moved (see "note:" messages in the example above) and the concept error
message does something pretty similar except, again, portably, i.e. being
able to give such error messages is one of the main ideas behind
introducing concepts.

GC> Here's a test case that gives no '-Wdefaulted-function-deleted'
GC> warning--in fact, if we comment out the assertion, clang compiles
GC> it successfully.

 Yes, of course, because class E doesn't default any functions. If it used
the rule of 5, rather than rule of 0, there would have been a warning here.

 I.e. I think your logic here is correct, but somewhat orthogonal to what
I'm saying: if you want to use the rule of 0, clang warning doesn't help
you, but I'm saying that clang warning helps you if you use the rule of 5
in the first place. So the choice is not between using rule of 0 and
relying on this warning or using rule of 5 and relying on it, but rather
between using rule of 5 and relying on the warning to tell us about any
problems or using rule of 0 and do something else (i.e. adding static
assets or concept checks).

GC> >  So now we know that you can do it. But does this still seem like a better
GC> > solution than just checking clang warning to you?
GC> 
GC> Yes, because I get it at step "2." above rather than step "3.".

 No, sorry, I have to point out that this is incorrect. If there is a
warning, it is given at step (2), of course, otherwise it wouldn't be very
useful.

GC> The ingenuity isn't mine:
GC>   
https://stackoverflow.com/questions/51901837/how-to-get-if-a-type-is-truly-move-constructible/51912859#51912859
GC> In olden days I would have credited the author. But this isn't usenet,
GC> and I can't determine who "C.M." is.

 People could (and did) stay anonymous on Usenet too, and even I have to
admit that referring to Stack Overflow links is much more convenient than
linking the Usenet messages, so all is not necessarily for the worse...


GC> AFAICT, to enforce a "concept" for a non-template class, we use
GC> static_assert() as in this example, adapted from:
GC> 
GC> https://www.foonathan.net/2021/07/concepts-structural-nominal/
GC> 
GC>    struct vec2 { … };
GC> // concept equality_comparable for vec2;     // we think this way...
GC>    static_assert(equality_comparable<vec2>); // ...but write this way

 Yes, as per https://en.cppreference.com/w/cpp/language/constraints

        > Each concept is a predicate, evaluated at compile time

so equality_comparable<vec2> is a constexpr bool.

GC> In this limited case (non-template classes), <concepts> gives us very
GC> little AFAICT, as there's little difference between these examples:
GC> 
GC>   #include <concepts>
GC>   struct vec2 { … };
GC>   static_assert(std::destructible<vec2>);
GC> 
GC>   #include <type_traits>
GC>   struct vec2 { … };
GC>   static_assert(std::is_destructible_v<vec2>);

 Are you sure? You should see the difference quite clearly if the assert
fails because the error messages will be much more detailed (and,
hopefully, helpful) when using the concept. Everything depends on the
concrete concept and the concrete problem preventing it from being
satisfied, of course, but IM-very-limited-E concept validation failure
directly points you to the source of the problem, while static assert
failure basically just tells you that something is wrong and then you're on
your own to find out what it is.

GC> It's a different story for class templates, but for non-template classes,
GC> is there really nothing more to it than this?

 AFAIK the idea of allowing better error message was an important part of
the rationale for introducing concepts into the language, and I think this
is pretty important. But, other than this, I don't think there is any
material difference between them.

 Regards,
VZ

Attachment: pgpsSns_r6kb8.pgp
Description: PGP signature


reply via email to

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