Why is `boost::container::flat_set` not `nothrow_move_constructible`? - c++

I was writing some code that has a generic container that requires elements to be nothrow_move_constructible.
I decided to add a static_assert that enforces this, just in case.
To my surprise I can't compile now when using boost::container::flat_set.
I assumed that this was just an oversight and I need a more recent boost verison, but it seems that actually they deliberately made it not safely movable:
See docs here:
http://www.boost.org/doc/libs/1_61_0/doc/html/boost/container/flat_set.html
You can see that they did update it to use R-value references and marked swap as noexcept, but they chose not to make the move ctor noexcept.
It appears that move assignment is conditionally noexcept. The condition appears to depend on the value type and on the allocator in some way.
What could be the rationale for not being nothrow move constructible? Is it just an oversight?

If the objects within a container are not nothrow_move_constructible then it is very dangerous to take an entire set of one container and relocate it to another under certain conditions (usually involving Allocators). If two containers were not constructed with the same allocator, then it is no longer safe to move memory from one container to another (think two containers from two different memory arenas).
Digging in to the current source, both the contract and the implementation are problematic:
//! <b>Effects</b>: Move constructs a flat_map.
//! Constructs *this using x's resources.
flat_map(BOOST_RV_REF(flat_map) x)
: m_flat_tree(boost::move(x.m_flat_tree))
{ ... }
So your current expectation that it could currently by nothrow is correct. But what they are doing is probably not right.
I can only guess that they are worried that they will have to do revisit this in the future and don't want to have to weaken the nothrow contract later.

Related

Where do standard library or compilers leverage noexcept move semantics (other than vector growth)?

Move operations should be noexcept; in the first place for intuitive and reasonable semantics. The second argument is runtime performance. From the Core Guidelines, C.66, "Make move operations noexcept":
A throwing move violates most people’s reasonably assumptions. A non-throwing move will be used more efficiently by standard-library and language facilities.
The canonical example for the performance-part of this guideline is the case when std::vector::push_back or friends need to grow the buffer. The standard requires a strong exception guarantee here, and this can only move-construct the elements into the new buffer if this is noexcept - otherwise, it must be copied. I get that, and the difference is visible in benchmarks.
However, apart from this, I have a hard time finding real-world evidence of the positive performance impact of noexcept move semantics. Skimming through the standard library (libcxx + grep), we see that std::move_if_noexcept exists, but it's almost not used within the library itself. Similarly, std::is_noexcept_swappable is merely used for fleshing out conditional noexcept qualifiers. This doesn't match existing claims, for example this one from "C++ High Performance" by Andrist and Sehr (2nd ed., p. 153):
All algorithms use std::swap() and std::move() when moving elements around, but only if the move constructor and move assignment are marked noexcept. Therefore, it is important to have these implemented for heavy objects when using algorithms. If they are not available and exception free, the elements will be copied instead.
To break my question into pieces:
Are there code paths in the standard library similar to the std::vector::push_back, that run faster when fed with std::is_nothrow_move_constructible types?
Am I correct to conclude that the cited paragraph from the book is not correct?
Is there an obvious example for when the compiler will reliably generate more runtime-efficient code when a type adheres to the noexcept guideline?
I know the third one might be a bit blurry. But if someone could come up with a simple example, this would be great.
Background: I refer to std::vector's use of noexcept as "the vector pessimization." I claim that the vector pessimization is the only reason anyone ever cared about putting a noexcept keyword into the language. Furthermore, the vector pessimization applies only to the element type's move constructor. I claim that marking your move-assignment or swap operations as noexcept has no "in-game effect"; leaving aside whether it might be philosophically satisfying or stylistically correct, you shouldn't expect it to have any effect on your code's performance.
Let's check a real library implementation and see how close I am to wrong. ;)
Vector reallocation. libc++'s headers use move_if_noexcept only inside __construct_{forward,backward}_with_exception_guarantees, which is used only inside vector reallocation.
Assignment operator for variant. Inside __assign_alt, the code tag-dispatches on is_nothrow_constructible_v<_Tp, _Arg> || !is_nothrow_move_constructible_v<_Tp>. When you do myvariant = arg;, the default "safe" approach is to construct a temporary _Tp from the given arg, and then destroy the currently emplaced alternative, and then move-construct that temporary _Tp into the new alternative (which hopefully won't throw). However, if we know that the _Tp is nothrow-constructible directly from arg, we'll just do that; or, if _Tp's move-constructor is throwing, such that the "safe" approach isn't actually safe, then it's not buying us anything and we'll just do the fast direct-construction approach anyway.
Btw, the assignment operator for optional does not do any of this logic.
Notice that for variant assignment, having a noexcept move constructor actually hurts (unoptimized) performance, unless you have also marked the selected converting constructor as noexcept! Godbolt.
(This experiment also turned up an apparent bug in libstdc++: #99417.)
string appending/inserting/assigning. This is a surprising one. string::append makes a call to __append_forward_unsafe under a SFINAE check for __libcpp_string_gets_noexcept_iterator. When you do s1.append(first, last), we'd like to do s1.resize(s1.size() + std::distance(first, last)) and then copy into those new bytes. However, this doesn't work in three situations: (1) If first, last point into s1 itself. (2) If first, last are exactly input_iterators (e.g. reading from an istream_iterator), such that it's known impossible to iterate the range twice. (3) If it's possible that iterating the range once could put it into a bad state where iterating the second time would throw. That is, if any of the operations in the second loop (++, ==, *) are non-noexcept. So in any of those three situations, we take the "safe" approach of constructing a temporary string s2(first, last) and then s1.append(s2). Godbolt.
I would bet money that the logic controlling this string::append optimization is incorrect. (EDIT: yes, it is.) See "Attribute noexcept_verify" (2018-06-12). Also observe in that godbolt that the operation whose noexceptness matters to libc++ is rv == rv, but the one it actually calls inside std::distance is lv != lv.
The same logic applies even harder in string::assign and string::insert. We need to iterate the range while modifying the string. So we need either a guarantee that the iterator operations are noexcept, or a way to "back out" our changes when an exception is thrown. And of course for assign in particular, there's not going to be any way to "back out" our changes. The only solution in that case is to copy the input range into a temporary string and then assign from that string (because we know string::iterator's operations are noexcept, so they can use the optimized path).
libc++'s string::replace does not do this optimization; it always copies the input range into a temporary string first.
function SBO. libc++'s function uses its small buffer only when the stored callable object is_nothrow_copy_constructible (and of course is small enough to fit). In that case, the callable is treated as a sort of "copy-only type": even when you move-construct or move-assign the function, the stored callable will be copy-constructed, not move-constructed. function doesn't even require that the stored callable be move-constructible at all!
any SBO. libc++'s any uses its small buffer only when the stored callable object is_nothrow_move_constructible (and of course is small enough to fit). Unlike function, any treats "move" and "copy" as distinct type-erased operations.
Btw, libc++'s packaged_task SBO doesn't care about throwing move-constructors. Its noexcept move-constructor will happily call the move-constructor of a user-defined callable: Godbolt. This results in a call to std::terminate if the callable's move-constructor ever actually does throw. (Confusingly, the error message printed to the screen makes it look as if an exception is escaping out the top of main; but that's not actually what's happening internally. It's just escaping out the top of packaged_task(packaged_task&&) noexcept and being halted there by the noexcept.)
Some conclusions:
To avoid the vector pessimization, you must declare your move-constructor noexcept. I still think this is a good idea.
If you declare your move-constructor noexcept, then to avoid the "variant pessimization," you must also declare all your single-argument converting constructors noexcept. However, the "variant pessimization" merely costs a single move-construct; it does not degrade all the way into a copy-construct. So you can probably eat this cost safely.
Declaring your copy constructor noexcept can enable small-buffer optimization in libc++'s function. However, this matters only for things that are (A) callable and (B) very small and (C) not in possession of a defaulted copy constructor. I think this describes the empty set. Don't worry about it.
Declaring your iterator's operations noexcept can enable a (dubious) optimization in libc++'s string::append. But literally nobody cares about this; and besides, the optimization's logic is buggy anyway. I'm very much considering submitting a patch to rip out that logic, which will make this bullet point obsolete. (EDIT: Patch submitted, and also blogged.)
I'm not aware of anywhere else in libc++ that cares about noexceptness. If I missed something, please tell me! I'd also be very interested to see similar rundowns for libstdc++ and Microsoft.
vector push_back, resize, reserve, etc is very important case, as it is expected to be the most used container.
Anyway, take look at std::fuction as well, I'd expect it to take advantage of noexcept move for small object optimization version.
That is, when functor object is small, and it has noexcept move constructor, it can be stored in a small buffer in std::function itself, not on heap. But if the functor doesn't have noexcept move constructor, it has to be on heap (and don't move when std::function is moved)
Overall, there ain't too many cases indeed.

Allocator Aware Container and propagate_on_container_swap

The std::allocator_traits template defines a few constants, like propagate_on_container_copy/move_assign to let other containers know whether they should copy the allocator of a second container during a copy or move operation.
We also have propagate_on_container_swap, which specifies whether the allocator should be copied during a swap operation.
Is it really necessary for an Allocator Aware container to check for allocator_traits<A>::propagate_on_container_swap in Container::swap()? Usually, I implement swap as follows:
Container::swap(Container& other)
{
Container tmp(std::move(other));
other = std::move(*this);
*this = std::move(tmp);
}
In other words, I simply implement swap in terms of move assignment. Since the move assignment operation already has to deal with allocator awareness (by checking propagate_on_container_move_assign), is it okay to implement Container::swap() like this, instead of writing a totally different swap function which explicitly checks for propagate_on_container_swap?
It is important to draw a distinction between requirements the standard places on your code vs the requirements it places on types provided by the implementor of the std::lib.
The container requirements specify how the std::containers must behave. However you are free to write your container however you like. Unless you input your container into std::code which requires the std::container behavior, you are good to go. There are just a couple of such places. For example if you adapt your Container with std::stack, then you will have to provide standard behavior if you expect the std::stack to behave according to the standard.
Getting back to your question, if you desire your Container to have the same behavior as one of the std::containers in this regard, then you will have to check and abide by all of the propagate_on traits, and all of the other allocator requirements. This is a non-trivial task. And I am not necessarily recommending it.
The std::containers will not perform a container move construction nor move assignment during swap. They will instead swap their internal representations. They will decide (at compile-time) based on propagate_on_container_swap whether or not they will swap allocators.
If propagate_on_container_swap is true, they will swap allocators and internal representations. Also in this case, swap will be noexcept in C++1z (we hope that is C++17) and forward.
If propagate_on_container_swap is false the allocators shall not be swapped, and need not even be Swappable. However the container internals are still swapped. In this case, if the two allocators do not compare equal, the behavior is undefined.
If you keep your Container::swap as it is, and create a std::stack based on your Container, the std::stack::swap will not have standard behavior. However, it will have the behavior of your Container::swap, and if that is fine with the client of said std::stack and whatever allocator they may be using, then no harm done. std::stack::swap will not behave in mysterious ways just because you didn't rigorously follow all of the intricate details for allocators that the std::containers are required to.
Sure, you can implement it using move-ctor and move-assignment of the container.
That's the way std::swap does it though, and there is absolutely no reason to re-write it instead of using the standard one.
Those propagate-constants allow omitting useless extra-work, and you are free t ignore the potential for optimization.
Just be aware that you are potentially doing too much work, aka your swap is potentially less efficient.

Making use of allocators in a custom container class

I'm developing a container-like class and I would like to make use of the standard allocator infrastructure much like the standard containers. On the net I find a lot of material about how to use the std::allocator class alone, or how to define a custom allocator for standard containers, but the material about how to make generically use of an standard conforming allocator is very rare, in particular in the context of C++11, where things seem to be much easier from the point of view of who writes a custom allocator, but more complex from the container's point of view.
So my question is about how to correctly make use of a standard conforming allocator in the most generic way, specifically:
First of all, when should I design a custom container in this way? Is there a sensible performance overhead (including missing optimization opportunities) in using the default allocator instead of plain new/delete?
Do I have to explicitly call contained objects' destructors?
How do I discriminate between stateful and stateless allocators?
How to handle stateful allocators?
When (if ever) are two instances interchangeable (when can I destroy with one instance the memory allocated with another one)?
They have to be copied when the container is copied?
They can/have to be moved when the container is moved?
In the container's move constructor and move assignment operator, when can I move the pointer to allocated memory, and when do I have to allocate different memory and move the elements instead?
Are there issues about exception safety in this context?
I'm specifically interested in an answer about the C++11 world (does it change anything in C++14?)
In all answers below, I'm assuming you want to follow the rules for C++11 std-defined containers. The standard does not require you to write your custom containers this way.
First of all, when should I design a custom container in this way? Is there a sensible performance overhead (including missing
optimization opportunities) in using the default allocator instead of
plain new/delete?
One of the most common and effective uses for custom allocators is to have it allocate off of the stack, for performance reasons. If your custom container can not accept such an allocator, then your clients will not be able to perform such an optimization.
Do I have to explicitly call contained objects' destructors?
You have to explicitly call allocator_traits<allocator_type>::destroy(alloc, ptr), which in turn will either directly call the value_type's destructor, or will call the destroy member of the allocator_type.
How do I discriminate between stateful and stateless allocators?
I would not bother. Just assume the allocator is stateful.
How to handle stateful allocators?
Follow the rules laid out in C++11 very carefully. Especially those for allocator-aware containers specified in [container.requirements.general]. The rules are too numerous to list here. However I'm happy to answer specific questions on any of those rules. But step one is get a copy of the standard, and read it, at least the container requirements sections. I recommend the latest C++14 working draft for this purpose.
When (if ever) are two instances interchangeable (when can I destroy with one instance the memory allocated with another one)?
If two allocators compare equal, then either can deallocate pointers allocated from the other. Copies (either by copy construction or copy assignment) are required to compare equal.
They have to be copied when the container is copied?
Search the standard for propagate_on and select_on_container_copy_construction for the nitty gritty details. The nutshell answer is "it depends."
They can/have to be moved when the container is moved?
Have to be for move construction. Move assignment depends on propagate_on_container_move_assignment.
In the container's move constructor and move assignment operator, when can I move the pointer to allocated memory, and when do I have to allocate different memory
and move the elements instead?
The newly move constructed container should have gotten its allocator by move constructing the rhs's allocator. These two allocators are required to compare equal. So you can transfer memory ownership for all allocated memory for which your container has a valid state for that pointer being nullptr in the rhs.
The move assignment operator is arguably the most complicated: The behavior depends on propagate_on_container_move_assignment and whether or not the two allocators compare equal. A more complete description is below in my "allocator cheat sheet."
Are there issues about exception safety in this context?
Yes, tons. [allocator.requirements] lists the allocator requirements, which the container can depend on. This includes which operations can and can not throw.
You will also need to deal with the possibility that the allocator's pointer is not actually a value_type*. [allocator.requirements] is also the place to look for these details.
Good luck. This is not a beginner project. If you have more specific questions, post them to SO. To get started, go straight to the standard. I am not aware of any other authoritative source on the subject.
Here is a cheat-sheet I made for myself which describes allocator behavior, and the container's special members. It is written in English, not standard-eze. If you find any discrepancies between my cheat sheet, and the C++14 working draft, trust the working draft. One known discrepancy is that I've added noexcept specs in ways the standard has not.
Allocator behavior:
C() noexcept(is_nothrow_default_constructible<allocator_type>::value);
C(const C& c);
Gets allocator from alloc_traits::select_on_container_copy_construction(c).
C(const C& c, const allocator_type& a);
Gets allocator from a.
C(C&& c)
noexcept(is_nothrow_move_constructible<allocator_type>::value && ...);
Gets allocator from move(c.get_allocator()), transfers resources.
C(C&& c, const allocator_type& a);
Gets allocator from a. Transfers resources if a == c.get_allocator().
Move constructs from each c[i] if a != c.get_allocator().
C& operator=(const C& c);
If alloc_traits::propagate_on_container_copy_assignment::value is true,
copy assigns allocators. In this case, if allocators are not equal prior
to assignment, dumps all resources from *this.
C& operator=(C&& c)
noexcept(
allocator_type::propagate_on_container_move_assignment::value &&
is_nothrow_move_assignable<allocator_type>::value);
If alloc_traits::propagate_on_container_move_assignment::value is true,
dumps resources, move assigns allocators, and transfers resources from c.
If alloc_traits::propagate_on_container_move_assignment::value is false
and get_allocator() == c.get_allocator(), dumps resources, and transfers
resources from c.
If alloc_traits::propagate_on_container_move_assignment::value is false
and get_allocator() != c.get_allocator(), move assigns each c[i].
void swap(C& c)
noexcept(!allocator_type::propagate_on_container_swap::value ||
__is_nothrow_swappable<allocator_type>::value);
If alloc_traits::propagate_on_container_swap::value is true, swaps
allocators. Regardless, swaps resources. Undefined behavior if the
allocators are unequal and propagate_on_container_swap::value is false.

Can we say bye to copy constructors?

Copy constructors were traditionally ubiquitous in C++ programs. However, I'm doubting whether there's a good reason to that since C++11.
Even when the program logic didn't need copying objects, copy constructors (usu. default) were often included for the sole purpose of object reallocation. Without a copy constructor, you couldn't store objects in a std::vector or even return an object from a function.
However, since C++11, move constructors have been responsible for object reallocation.
Another use case for copy constructors was, simply, making clones of objects. However, I'm quite convinced that a .copy() or .clone() method is better suited for that role than a copy constructor because...
Copying objects isn't really commonplace. Certainly it's sometimes necessary for an object's interface to contain a "make a duplicate of yourself" method, but only sometimes. And when it is the case, explicit is better than implicit.
Sometimes an object could expose several different .copy()-like methods, because in different contexts the copy might need to be created differently (e.g. shallower or deeper).
In some contexts, we'd want the .copy() methods to do non-trivial things related to program logic (increment some counter, or perhaps generate a new unique name for the copy). I wouldn't accept any code that has non-obvious logic in a copy constructor.
Last but not least, a .copy() method can be virtual if needed, allowing to solve the problem of slicing.
The only cases where I'd actually want to use a copy constructor are:
RAII handles of copiable resources (quite obviously)
Structures that are intended to be used like built-in types, like math vectors or matrices -
simply because they are copied often and vec3 b = a.copy() is too verbose.
Side note: I've considered the fact that copy constructor is needed for CAS, but CAS is needed for operator=(const T&) which I consider redundant basing on the exact same reasoning;
.copy() + operator=(T&&) = default would be preferred if you really need this.)
For me, that's quite enough incentive to use T(const T&) = delete everywhere by default and provide a .copy() method when needed. (Perhaps also a private T(const T&) = default just to be able to write copy() or virtual copy() without boilerplate.)
Q: Is the above reasoning correct or am I missing any good reasons why logic objects actually need or somehow benefit from copy constructors?
Specifically, am I correct in that move constructors took over the responsibility of object reallocation in C++11 completely? I'm using "reallocation" informally for all the situations when an object needs to be moved someplace else in the memory without altering its state.
The problem is what is the word "object" referring to.
If objects are the resources that variables refers to (like in java or in C++ through pointers, using classical OOP paradigms) every "copy between variables" is a "sharing", and if single ownership is imposed, "sharing" becomes "moving".
If objects are the variables themselves, since each variables has to have its own history, you cannot "move" if you cannot / don't want to impose the destruction of a value in favor of another.
Cosider for example std::strings:
std::string a="Aa";
std::string b=a;
...
b = "Bb";
Do you expect the value of a to change, or that code to don't compile? If not, then copy is needed.
Now consider this:
std::string a="Aa";
std::string b=std::move(a);
...
b = "Bb";
Now a is left empty, since its value (better, the dynamic memory that contains it) had been "moved" to b. The value of b is then chaged, and the old "Aa" discarded.
In essence, move works only if explicitly called or if the right argument is "temporary", like in
a = b+c;
where the resource hold by the return of operator+ is clearly not needed after the assignment, hence moving it to a, rather than copy it in another a's held place and delete it is more effective.
Move and copy are two different things. Move is not "THE replacement for copy". It an more efficient way to avoid copy only in all the cases when an object is not required to generate a clone of itself.
Short anwer
Is the above reasoning correct or am I missing any good reasons why logic objects actually need or somehow benefit from copy constructors?
Automatically generated copy constructors are a great benefit in separating resource management from program logic; classes implementing logic do not need to worry about allocating, freeing or copying resources at all.
In my opinion, any replacement would need to do the same, and doing that for named functions feels a bit weird.
Long answer
When considering copy semantics, it's useful to divide types into four categories:
Primitive types, with semantics defined by the language;
Resource management (or RAII) types, with special requirements;
Aggregate types, which simply copy each member;
Polymorphic types.
Primitive types are what they are, so they are beyond the scope of the question; I'm assuming that a radical change to the language, breaking decades of legacy code, won't happen. Polymorphic types can't be copied (while maintaining the dynamic type) without user-defined virtual functions or RTTI shenanigans, so they are also beyond the scope of the question.
So the proposal is: mandate that RAII and aggregate types implement a named function, rather than a copy constructor, if they should be copied.
This makes little difference to RAII types; they just need to declare a differently-named copy function, and users just need to be slightly more verbose.
However, in the current world, aggregate types do not need to declare an explicit copy constructor at all; one will be generated automatically to copy all the members, or deleted if any are uncopyable. This ensures that, as long as all the member types are correctly copyable, so is the aggregate.
In your world, there are two possibilities:
Either the language knows about your copy-function, and can automatically generate one (perhaps only if explicitly requested, i.e. T copy() = default;, since you want explicitness). In my opinion, automatically generating named functions based on the same named function in other types feels more like magic than the current scheme of generating "language elements" (constructors and operator overloads), but perhaps that's just my prejudice speaking.
Or it's left to the user to correctly implement copying semantics for aggregates. This is error-prone (since you could add a member and forget to update the function), and breaks the current clean separation between resource management and program logic.
And to address the points you make in favour:
Copying (non-polymorphic) objects is commonplace, although as you say it's less common now that they can be moved when possible. It's just your opinion that "explicit is better" or that T a(b); is less explicit than T a(b.copy());
Agreed, if an object doesn't have clearly defined copy semantics, then it should have named functions to cover whatever options it offers. I don't see how that affects how normal objects should be copied.
I've no idea why you think that a copy constructor shouldn't be allowed to do things that a named function could, as long as they are part of the defined copy semantics. You argue that copy constructors shouldn't be used because of artificial restrictions that you place on them yourself.
Copying polymorphic objects is an entirely different kettle of fish. Forcing all types to use named functions just because polymorphic ones must won't give the consistency you seem to be arguing for, since the return types would have to be different. Polymorphic copies will need to be dynamically allocated and returned by pointer; non-polymorphic copies should be returned by value. In my opinion, there is little value in making these different operations look similar without being interchangable.
One case where copy constructors come in useful is when implementing the strong exception guarantees.
To illustrate the point, let's consider the resize function of std::vector. The function might be implemented roughly as follows:
void std::vector::resize(std::size_t n)
{
if (n > capacity())
{
T *newData = new T [n];
for (std::size_t i = 0; i < capacity(); i++)
newData[i] = std::move(m_data[i]);
delete[] m_data;
m_data = newData;
}
else
{ /* ... */ }
}
If the resize function were to have a strong exception guarantee we need to ensure that, if an exception is thrown, the state of the std::vector before the resize() call is preserved.
If T has no move constructor, then we will default to the copy constructor. In this case, if the copy constructor throws an exception, we can still provide strong exception guarantee: we simply delete the newData array and no harm to the std::vector has been done.
However, if we were using the move constructor of T and it threw an exception, then we have a bunch of Ts that were moved into the newData array. Rolling this operation back isn't straight-forward: if we try to move them back into the m_data array the move constructor of T may throw an exception again!
To resolve this issue we have the std::move_if_noexcept function. This function will use the move constructor of T if it is marked as noexcept, otherwise the copy constructor will be used. This allows us to implement std::vector::resize in such a way as to provide a strong exception guarantee.
For completeness, I should mention that C++11 std::vector::resize does not provide a strong exception guarantee in all cases. According to www.cplusplus.com we have the the follow guarantees:
If n is less than or equal to the size of the container, the function never throws exceptions (no-throw guarantee).
If n is greater and a reallocation happens, there are no changes in the container in case of exception (strong guarantee) if the type of the elements is either copyable or no-throw moveable.
Otherwise, if an exception is thrown, the container is left with a valid state (basic guarantee).
Here's the thing. Moving is the new default- the new minimum requirement. But copying is still often a useful and convenient operation.
Nobody should bend over backwards to offer a copy constructor anymore. But it is still useful for your users to have copyability if you can offer it simply.
I would not ditch copy constructors any time soon, but I admit that for my own types, I only add them when it becomes clear I need them- not immediately. So far this is very, very few types.

Can queue::pop return a value now?

I know std::queue::pop() returns void. For two reasons:
exception safety: something might throw after removing the element
to be able to return the value by reference
Fine.
Now if I understand the new C++11 move semantics correctly, the second is no longer a valid argument.
So... the only thing preventing std::queue to have a pop-like function returning the value lies in the possibility that the move constructor throws?
I have a hard time thinking of situations where such a move constructor would throw. Who knows of an example?
I guess the same goes for std::stack::pop(), std::vector::pop_front(), std::vector::pop_back(), std::deque::pop_front(), std::deque::pop_back(), std::list::pop_front(), std::list::pop_back() and what not.
There aren't many cases where std::move() can throw in the standard library but there are cases. For example, if the container uses a stateful allocator, its children also use this allocator, but it won't be moved to a result: this would rather get a default constructed version of an allocator (if I remove correctly). Given that the allocators are stateful this means that the object can't be moved and thus the move construction fails with an exception. Why does this type then have a move constructor? Well, because it might be instantiated with non-stateful allocator in which case moving won't throw. In addition, once we move to user defined classes we have no idea under which condition moving them might throw.
Using clever SFINAE techniques it would indeed be possible to have an atomic non-throwing pop_and_move() for just datatypes that implement no-throwing move or no-throwing copy.
There is even a noexcept() construct available to see if something might throw.
One of the new concepts in C++11 in particular that extends SFINAE is that if the body doesn't compile the function doesn't exist. Thus one could implement based on noexcept().
I would say for backward compatibility the function would need a new name, which therefore allows it to co-exist with the existing functionality of calling them separately, not breaking containers of types that do not have the semantics to allow it.
Another problem is, that not every class really benefits from moving, i.e., they might only have a copy ctor.
struct DontLikeMoves{
// some data, whatever...
DontLikeMoves(DontLikeMoves const& other){
// might throw, who knows!
// and this will even get called for rvalues
}
};