A move constructor is somewhat special, because there is an obvious fallback. In a situation where you cannot allow a move to throw an exception you can call a noexcept
move constructor and when the move constructor is not noexcept
you can fallback to a copy. Hence, declaring the move constructor that does not throw exceptions as noexcept
is a potential optimization.
For example std::vector::push_back
does try to give a strong exception guarantee (from cppreference):
If an exception is thrown (which can be due to Allocator::allocate()
or element copy/move constructor/assignment), this function has no effect (strong exception guarantee).
However, since C++11:
If T's move constructor is not noexcept
and T is not CopyInsertable into *this
, vector will use the throwing move constructor. If it throws, the guarantee is waived and the effects are unspecified.
Remember that pushing an element may require reallocations, ie to copy/move all elements to different memory location.
The above means: If the type is CopyInsertable, ie one has a choice between copying and moving, the vector
will move them when the move constructor is noexcept
. In this case you can gain performance by declaring the move constructor noexcept
.
When the element type cannot be copied the move constuctor is used in any case (and the vector relaxes its exception guarantee, thanks to François Andrieux for making me aware of insert
with similar balancing of exception safety vs performance). In this case what you gain by a noexcept
move constructor is a stronger exception guarantee.
std::vector::push_back
and similar examples is why there is so much discussion about declaring a move constructor noexcept
.
For <
there is no such obvious fallback that would be more expensive when <
is not noexcept
. Either you can call <
or you can't.
Otherwise <
has the same advantages as any other noexcept
method, that the compiler knows it does never throw (and if it does std::terminate
is called, but the usual stack unwinding does not necessarily take place).