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: Greg Chicares
Subject: Re: [lmi] Detecting whether move semantics actually take place
Date: Thu, 21 Jul 2022 00:53:01 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.8.0

On 7/18/22 16:19, Vadim Zeitlin wrote:
> On Mon, 18 Jul 2022 15:48:14 +0000 Greg Chicares <gchicares@sbcglobal.net> 
> wrote:
[...]
> GC> Let me pose a hypothetical question first: does this line
> GC>      db = std::move(da);   // D& operator=(D&&)
> GC> actually perform a move and not a copy, in a context where
> GC> class D defaults all special member functions?
> 
>  If the defaulted move assignment operator actually exists and is not
> deleted, then this will perform a move, yes.

The question I really want to ask is: is it possible in C++20 to answer
that question for a different class E of which we know nothing?

>  If you wanted to ensure that a copy doesn't happen here, you'd have to
> delete the (usual) assignment operator. But if you want to allow moving
> without requiring it, I indeed don't see any way to ascertain that the
> value is moved.

Here it's important to explain why I'm asking this question.

The number of classes in lmi is very approximately five hundred:
  $git grep '^\<class\>' *.?pp |wc -l
  527
Most were written for the C++98 dialect, and move members are generally
non-declared. I suspect that many of them have the property that, if we
cause them (by whatever means) to acquire move member functions, then
those functions would actually use move semantics.

Suppose lmi copies an object, and I want to consider moving it instead.
I can write the move operation, but will move semantics actually take
place, or will the compiler silently substitute a copy to fulfill my
move request? I don't simply want to make the request; I want to know
how it's fulfilled. If moving is inherently impossible for some class,
I'd like to know that before I waste time trying to move it. Similarly,
I'd like to know whether moving is only incidentally impossible: e.g.,
 - because I've made the move functions inaccessible; or
 - because I've deleted them for no good reason, just because I didn't
     want to spend time considering them; or
 - because I made a base class unmoveable for some such reason; or
 - because I casually added a 'const&' data member for no strong
     reason, rendering the class unassignable;
because then I might want to consider removing that incidental cause.

For example, can I profitably move a Ledger? It looks like I can move
one, but will doing so substitute move semantics for copy semantics
that might be orders of magnitude slower? Alternatively: I think I
solved that efficiency problem long ago by holding a Ledger in a
shared_ptr; if I only pass that shared_ptr on one line of code, then
could I dispense with the shared_ptr and just do a move assignment?

Of course, the only way to be certain whether a revision improves
efficiency is to measure it. However, I might imagine many revisions
that seem potentially profitable, and before spending time on any
candidate, I'd like to know if I'd surely be wasting my time because
the compiler is going to substitute a copy anyway. If I could write
  static_assert(move_will_actually_use_move_semantics<T>);
then I could save that waste of time (or consider remediating the
reason why such an assertion fails).

We have
  std::is_move_constructible
  std::is_move_assignable
but they don't perform this job: they say that I can request a move,
but not that I can actually get move semantics.

Searching the web for an answer leads me to believe that most people
think the 'trait' I want cannot be implemented. I've tried to do it
anyway:
  https://git.savannah.nongnu.org/cgit/lmi.git/log/?h=odd/move_semantics
and I'd like to know what you think, because this is tricky stuff and
I might simply be deceiving myself.

The technique uses a template class:

  template<typename T>
  struct ambiguator
  {
      operator T const&();
      operator T&&();
  };

such that
  T t {ambiguator<T>{}};
would be a compile-time error if both copy and move constructors are
candidates in overload resolution. We can determine whether such a
compile-time error would occur, without actually triggering one, thus:
  bool equiplausible = !std::is_constructible_v<T, ambiguator<T>>;

The tests on that branch attempt to establish that the longed-for
  move_will_actually_use_move_semantics<T>
trait can be implemented as
  equiplausibly_constructible<T> && std::is_move_constructible<T>
and similarly for move assignment. Or have I only found fool's gold?


reply via email to

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