Std :: move () calls copy-ctor when there is no move-ctor. Why and how to prevent this?

I want to know if there is a safe programming practice that would warn the encoder about this subtle behavior when this happens or, better yet, avoid it in the first place.

The user struct A may not understand that the move constructor is missing. When they try to call the missing ctor, they receive neither a compiler warning nor an indication of the runtime that a copy of ctor is called instead.

The answer below explains the transformation that is taking place, but I see no reason for it to be good. If there was no constructor that takes the link constant as a parameter, there would be a compile-time error, and not just permission for a non-constant link version. So, why the attempt to use the displacement semantics does not lead to a compile-time error if the displacement semantics are not implemented in the class?

Is there a way to avoid this behavior with some compile-time option, or at least a way to detect it at runtime?

It was possible to assert(source is null) after moving if they were expecting a problem, but this is true for many problems.

An example when:

 struct A { A() {...} A(A &a) {...} A(A const & A) {...} }; 

built as in:

 A a1; A a2 = std::move(a1); //calls const copy (but how would I know?) 

this leads to a call to the const version of the called ctor copy. Now two objects can have a pointer to one resource, while one of them most likely calls it a destructor soon.

+6
source share
4 answers

Since std::move returns the value of rvalue, which is converted to const ref, so copying ctor is quietly called. You can fix this in several ways.

  • An easy way, if your class does not have dynamic allocation, just use the default mctor like this. A(A &&) = default;
  • getting rid of const in the encoder is not a good idea, but it will not compile than

  • Just implement your own move constructor, why do you jump to an object if it doesn't have an mctor


This is a little off topic, but you can also qualify a member function to only work with a mutable lvalue value.

int func()&;

If you are dealing with outdated code, compilation time checking is performed here

+4
source

There is no safe programming practice that would warn about this because executing a copy after std::move incredibly common and normal. Adding a warning will cause almost everything in the <algorithm> begin to warn of dismissal, and we will need a completely new function that we will use that works just like std::move . Many algorithms rely on copying if there is no move constructor, or moves in the reverse order, and std::move used for this. At best, you should argue about the existence of std::force_move .

Secondly, this is completely unnecessary. Take this modified version of the code you showed.

 void legacy_api(A a1) { A a2 = std::move(a1); ... 

You say that this is a problem, that she subtly used an expensive copy, and not a subtle move. I do not agree with what you need, this is a new instance of the object, potentially destroying the previous one. If the code needs another instance, it needs another instance. Regardless of whether it was destroyed by the previous one, it should be completely irrelevant. If the code moves when it doesn’t need another instance, then the lagacy API is clearly written from scratch, and the above warning will not help. I cannot think of any reason why the function will require moving, but there is no copy, there is no purpose for such a thing.

Finally, "Now two objects may have a pointer to one resource, while one of them will probably call it a destructor soon." No, if that happens, it means that A copy constructor has an error, period. Correct the errors in the code and the problems will disappear. Like magic!

+3
source

Since std :: move returns a reference to rvalue (as in & &), which does not convert to a link to lvalue (A &), but is converted to a constant reference to lvalue (const A &).

int x = 5. Here 5 is an rvalue and can never be bound to an lvalue, and this also takes place in your example because you used std :: move (a1)

+1
source

If you are the owner of the class that you want to move, but this class does not have a move constructor because you do not want to move it, you can write:

 class A { public: // Other constuctors A(A&&) = delete; }; 

So, when you try to move A , if I am not mistaken, a compiler error occurs. If you want the object to always be moved, you must of course write your own move constructor or enable the default move constructor:

  A(A&&) = default; 

If you do not own the class, I think there are no direct ways to avoid calling the copy constructor. Perhaps the possible idiom to force her is this:

 template<bool b, typename T> using enabling = typename std::enable_if<b, T>::type; template<typename T> constexpr bool movable() { return std::is_move_constructible<T>::value; } template<typename T> enabling<movable<T>(), T&&> movify(T&& t) { return std::move(t); } A a1; A a2 = movify(a1); 

That should work. I have not tested this, but I think you caught this idea.

+1
source

Source: https://habr.com/ru/post/951851/


All Articles