This is a bit tricky. All std::vector<T>
functions that can increase the size of the vector have to do it in an exception-safe way if either of these two things are true:
T
has a move constructor that guarantees it will never throw any exceptions; or,
T
has a copy constructor.
So in most implementations, if T
has a move constructor declared nothrow
or equivalent, vector
will use the move constructor of T
for those operations. If not, and T
has a copy constructor, vector
will use the copy constructor, even if T
has a move constructor.
And the problem here is that std::queue
always declares it has a copy constructor, even if that copy constructor can't actually be instantiated, and always declares it has a move constructor that might throw, even if the container member's move constructor guarantees it won't throw.
The Standard specifies these in [queue.defn] as:
namespace std {
template<class T, class Container = deque<T>>
class queue {
// ...
public:
explicit queue(const Container&);
explicit queue(Container&& = Container());
// ...
};
}
This class template definition could be improved in a couple of ways to be more "SFINAE-friendly" and avoid issues like the one you ran into. (Maybe somebody could check for other classes with similar issues and submit a proposal to the Library Working Group.)
Change the move constructor to promise not to throw if the Container
type makes the same promise, typically done with language like:
explicit queue(Container&& rhs = Container()) nothrow(see below);
Remarks: The expression inside noexcept
is equivalent to is_nothrow_move_constructible_v<Container>
.
Change the copy constructor to be deleted if the Container
type is not copyable, typically done with language like:
explicit queue(const Container&);
Remarks: This constructor shall be defined as deleted unless is_copy_constructible_v<Container>
is true
.
push_back
makes a copy. To create an object in place, useemplace_back
. – Ben Voigtstd::queue
is replaced bystd::vector
; doesn't work with the default queue underlier ofstd::deque
. But it does work fine tomove
adeque
that isn't in a vector. Weird – M.Mdeque
that isn't in avector
. The operation will not benoexcept
however.vector
just doesn't like potentially throwing moves (see aschepler's answer for details). – Arne Vogel