[core] [noncopyable] Add nonmoveable?
In addition to noncopyable, I sometimes want to force classes to be nonmoveable as well (mainly node-type classes in tree structures which other classes point to). Therefore I'd suggest adding a cousin to noncopyable; boost::nonmoveable which simply prevents an instance to be nonmoveable (as well as noncopyable). Note; even due the delete modifier were added in C++11 I still think inheriting boost:noncopyable\nonmoveable syntactically nicer than manually marking the copy\move constructors\assignment operators delete. /Viktor Sehr
On 04/25/17 13:10, Viktor Sehr via Boost wrote:
In addition to noncopyable, I sometimes want to force classes to be nonmoveable as well (mainly node-type classes in tree structures which other classes point to). Therefore I'd suggest adding a cousin to noncopyable; boost::nonmoveable which simply prevents an instance to be nonmoveable (as well as noncopyable).
Note; even due the delete modifier were added in C++11 I still think inheriting boost:noncopyable\nonmoveable syntactically nicer than manually marking the copy\move constructors\assignment operators delete.
I think in C++11 noncopyable should be considered deprecated and generally avoided. It affects class hierarchy, adds an extra namespace to ADL and may not be optimized away with EBO. I would even avoid it in C++03 as well. It follows that nonmoveable makes no sense in C++11. Just use the language features you have.
On 25/04/2017 22:23, Andrey Semashev via Boost wrote:
On 04/25/17 13:10, Viktor Sehr via Boost wrote:
In addition to noncopyable, I sometimes want to force classes to be nonmoveable as well (mainly node-type classes in tree structures which other classes point to). Therefore I'd suggest adding a cousin to noncopyable; boost::nonmoveable which simply prevents an instance to be nonmoveable (as well as noncopyable).
Note; even due the delete modifier were added in C++11 I still think inheriting boost:noncopyable\nonmoveable syntactically nicer than manually marking the copy\move constructors\assignment operators delete.
I think in C++11 noncopyable should be considered deprecated and generally avoided. It affects class hierarchy, adds an extra namespace to ADL and may not be optimized away with EBO. I would even avoid it in C++03 as well. It follows that nonmoveable makes no sense in C++11. Just use the language features you have.
boost::noncopyable has protection so it won't ADL to the boost:: namespace. Also, since it does not explicitly declare move constructors either way, anything that inherits from boost::noncopyable is also non-moveable by default, which is usually what you want. The C++11 way, however, would be to use a std::unique_ptr as a private member of your class (eg. for PIMPL), which automatically makes your class moveable but not copyable via Rule of Zero. Or use shared_ptr if you want your class to be copyable but have all copies point to a shared implementation (handle-body or reference idiom).
On 04/26/17 03:00, Gavin Lambert via Boost wrote:
On 25/04/2017 22:23, Andrey Semashev via Boost wrote:
I think in C++11 noncopyable should be considered deprecated and generally avoided. It affects class hierarchy, adds an extra namespace to ADL and may not be optimized away with EBO. I would even avoid it in C++03 as well. It follows that nonmoveable makes no sense in C++11. Just use the language features you have.
boost::noncopyable has protection so it won't ADL to the boost:: namespace.
Yes, but it doesn't remove the dummy namespace from ADL.
Also, since it does not explicitly declare move constructors either way, anything that inherits from boost::noncopyable is also non-moveable by default, which is usually what you want.
Deleted copy constructor/assignment have the same property.
The C++11 way, however, would be to use a std::unique_ptr as a private member of your class (eg. for PIMPL), which automatically makes your class moveable but not copyable via Rule of Zero.
Or use shared_ptr if you want your class to be copyable but have all copies point to a shared implementation (handle-body or reference idiom).
That's fine if your design already employs pimpl (or you're willing to change it that way). But otherwise why would you want to add a data member to implement movability?
On 26/04/2017 20:53, Andrey Semashev wrote:
Yes, but it doesn't remove the dummy namespace from ADL.
As long as no other symbols are defined in that namespace, what does it matter?
Deleted copy constructor/assignment have the same property.
Yes, and that's better if you're writing a class that (or in a library that) otherwise does not depend on Boost in any way. But if you're taking a dependency on Boost anyway, then boost::noncopyable is less typing than explicitly deleting constructors.
That's fine if your design already employs pimpl (or you're willing to change it that way). But otherwise why would you want to add a data member to implement movability?
If your data members aren't the reason you're constraining movability, then why do it at all? And if they *are* the reason you're constraining movability, then you should separate the memory-management concerns of the class from the other concerns (SRP), which is where the smart pointer types come in. It doesn't have to be "true" PIMPL (where the pointer is the only published member) to take advantage of these behaviours.
On 27/04/2017 11:23, I wrote:
If your data members aren't the reason you're constraining movability, then why do it at all?
Incidentally, including a mutex in the class members (which is probably fairly common in multithreaded applications or libraries) also inherently constrains the class; mutexes (whether std::mutex or from Boost.Thread) are also neither copyable nor movable, and these traits pass on to the containing class by default.
On 04/27/17 02:23, Gavin Lambert via Boost wrote:
On 26/04/2017 20:53, Andrey Semashev wrote:
Yes, but it doesn't remove the dummy namespace from ADL.
As long as no other symbols are defined in that namespace, what does it matter?
It adds more work to the compiler.
Deleted copy constructor/assignment have the same property.
Yes, and that's better if you're writing a class that (or in a library that) otherwise does not depend on Boost in any way. But if you're taking a dependency on Boost anyway, then boost::noncopyable is less typing than explicitly deleting constructors.
Well, the gain in the number of keystrokes does not outweigh the drawbacks, IMO.
That's fine if your design already employs pimpl (or you're willing to change it that way). But otherwise why would you want to add a data member to implement movability?
If your data members aren't the reason you're constraining movability, then why do it at all?
Because the class is _supposed_ to be non-moveable according to its semantics. For example, std::mutex is non-movable not because its pthread_mutex_t member is non-movable (it's not) but because mutexes should not be movable or copyable as that doesn't make sense and plainly dangerous in the intended pattern of use. Frequently, I mark my types non-copyable, and by consequence non-movable, because I have not yet determined the use case and semantics for copying/moving the object, and the default implementation would be inappropriate. Better mark it non-copyable/non-movable for now and think about it later, when the need appears.
And if they *are* the reason you're constraining movability, then you should separate the memory-management concerns of the class from the other concerns (SRP), which is where the smart pointer types come in. It doesn't have to be "true" PIMPL (where the pointer is the only published member) to take advantage of these behaviours.
IMO, movability or non-movability is not the reason to convert a class to pimpl (pure or not). Very rarely I see a case when an object is not movable because of its members (e.g. a mutex) yet it is required to be movable by the rest of the program. When that happens it's usually a sign of a design problem.
On 27/04/2017 20:53, Andrey Semashev wrote:
On 04/27/17 02:23, Gavin Lambert wrote:
As long as no other symbols are defined in that namespace, what does it matter?
It adds more work to the compiler.
Not really, no. Only free functions can possibly participate in ADL, and there aren't any in that namespace, so at worst this is a single lookup into an empty dictionary. I doubt you could even measure it.
Yes, and that's better if you're writing a class that (or in a library that) otherwise does not depend on Boost in any way. But if you're taking a dependency on Boost anyway, then boost::noncopyable is less typing than explicitly deleting constructors.
Well, the gain in the number of keystrokes does not outweigh the drawbacks, IMO.
I'm still waiting to hear *any* drawbacks. And it's not just the keystrokes, it's also more self-documenting to see "noncopyable" than to see deleted copy constructors. It's also less error-prone -- I've seen quite a few cases in C++98 code where someone *intended* to declare a private copy constructor to do the same thing, but they forgot something; typically either forgetting to declare the copy-assignment operator or forgetting that it needed to be a reference argument, with the result that it didn't actually do anything useful. Nothing in C++11 reduces those risks. Using noncopyable does. (Another one is renaming the class while forgetting to change the copy-constructor parameter, making it a regular constructor instead of a copy-constructor.)
Because the class is _supposed_ to be non-moveable according to its semantics. For example, std::mutex is non-movable not because its pthread_mutex_t member is non-movable (it's not) but because mutexes should not be movable or copyable as that doesn't make sense and plainly dangerous in the intended pattern of use.
Actually, depending on implementation, the pthread_mutex_t might be non-movable. Futexes, for example, use the address of the mutex as part of their contract with the kernel, so moving it would break behaviour. Also, as a non-POD opaque data structure, it's definitely unsafe to memcpy it, and neither Windows nor Pthreads provide a method to move or copy one (the closest you can get is to destroy one and create another, which is typically undefined behaviour if the lock is held or there are any waiters, or if there might be any concurrent access to it), so even when not using futexes there is no way to move one safely anyway. So it is most definitely always non-movable.
IMO, movability or non-movability is not the reason to convert a class to pimpl (pure or not). Very rarely I see a case when an object is not movable because of its members (e.g. a mutex) yet it is required to be movable by the rest of the program. When that happens it's usually a sign of a design problem.
That's not what I was saying. I was saying that if you're making your class pimpl (or mostly-pimpl) anyway (as is common in some domains for other reasons), this can automatically specify the copy/move traits of your class without having to do anything special. (And that you can consider this when choosing which type of pimpl to use for a given class.) This is a side-benefit (and falls under Rule of Zero and SRP, as I said before), but not a motivation in itself.
Viktor Sehr wrote:
In addition to noncopyable, I sometimes want to force classes to be nonmoveable as well (mainly node-type classes in tree structures which other classes point to).
Every class that derives from noncopyable is also automatically nonmoveable, so it's not clear what you're asking.
participants (4)
-
Andrey Semashev
-
Gavin Lambert
-
Peter Dimov
-
Viktor Sehr