JUCE move semantics?

I always thought that when an object is moved it is left in “partially formed” state ; you can only delete or reassign it. I read in Nicolai Josuttis "C++ Move Semantics” book that in the STL (and generally) a move operation must be implemented in order to let the object in “valid but unspecified” state. Meaning that the invariants must be maintained. :cold_face:

It is reported also in Herb Sutter blog below.

  • Is JUCE conforms to the STL requirements?
  • Is people here conforms to the STL requirements?

(I think that keeping the invariants in unbroken state ruins the benefit of moving.)

Not too familiar with the STL requirements, but I find Clang-Tidy is fairly reliable in finding cases where move semantics could be utilised. E.g. clang-tidy - modernize-pass-by-value — Extra Clang Tools 22.0.0git documentation

I think other static analysers like sonarlint are pretty good at catching when you’re trying to use something that’s been moved-from.

According to blog above, in the STL you are allowed to use (a subset of the methods from) something that has been moved-from… :laughing: (and it is required to implement move like this).

Sure, but I’d much rather have a tool that can give me a build error to address rather than trying to remember when you can or can’t use something that’s been moved-from. Even if the tool sometimes gives false-positives, it’s still the most reliable way to ensure working software.

https://clang.llvm.org/extra/clang-tidy/checks/bugprone/use-after-move.html

This quote from Sutter is great:

Move is just another non-constfunction that might (or might not) change the value of the source object.

If one needs to check if std::move was used or not, basically means that something’s wrong with a program design.

Very interesting paper (if you care about this stuff)…

Interesting, indeed. The whole PDF reads more like a rant: the author complains that user-defined classes break after std::move and argues that the C++ standard should somehow (semantically?) guarantee that even such class designs remain fully valid. But in reality, the standard only requires that moved-from objects are valid but unspecified — preserving user-defined invariants is the responsibility of the class designer, not the language.

Long version, less informal, but less readable. :grinning_face_with_smiling_eyes:

EDIT: Very interesting and easier…

For exhaustivity:

I don’t quite get what’s the issue here? If you successfully(*) move an object, you should consider it unusable after that anyway. IMHO, it’s just tiresome language lawyering what the exact state of the object will be after a move. :person_shrugging:

Have you encountered some practical issue with some Juce class or classes because of this?

(*) By successful, I mean the object internals were actually moved into another object and not just copied.

2 Likes

Nope (for now and probably for ever).

I think it’s pretty annoying, that ValueTree doesn’t move the listener list as a move operation.

Would enable us to write std::vector and similar without wrapping in a unique_ptr.

But this issue seems to be a bit off-topic for this thread, if I understand the OP correctly.

I think this is the misconception that’s being pointed out in these blogs - “moving” an object in C++ doesn’t necesserily mean its data members are altered in any way. There are cases where a “moved-from” object would be left in an invalid state, but also cases where a “moved-from” object is left exactly as it was.

I agree with this though, and is why I like to use the linters I mentioned about to catch these things. Moving is more of a note to the reader to say “I’m done with this thing so I’m handing over to some other object”.

1 Like

I imagine this is to prevent issues where the listeners wouldn’t be able to remove themselves as listeners after the tree has been moved?

I think also that a moved-from object should not be used anymore (except for destruction or assignment). But AFAIK, Sutter, Hinnant, and Josuttis (people with large audience) teach that a moved-from object should be usable for methods with no preconditions (and not only into the STL). Thus, users of your library (and coworkers) should be aware of your assumptions to avoid bad surprises. That was the motivation of the original question. Which move semantics is adopted by JUCE classes?

I think in many cases the object would still be in a valid but unspecified state, and I would expect destruction and assignment to always work. However, I wouldn’t feel comfortable making a blanket statement. If we noticed we were leaving an object in some kind of invalid state following a move, I can imagine it would spark discussion in the MR. If we agreed, that in this instance, it was the best/right thing to do, we might add jasserts to help catch misuse. Although I can’t think of case where this has actually happened.

1 Like