This is a multifaceted question, so bear with me while I look at various aspects.
The standard library expects all user types to always provide a basic exception guarantee. This guarantee states that when an exception is thrown, the objects involved are still in a valid, if unknown, state that no resources are leaking, that no fundamental language invariants are violated, and that a frightening action has not occurred at a distance (the last one of them is not part of the formal definition, but this is actually an implied assumption).
Consider the copy constructor for the Foo class:
Foo(const Foo& o);
If this constructor throws, the basic exception guarantee gives you the following knowledge:
- New object not created. If the constructor throws, an object is not created.
o not been changed. His only participation here is a reference to const, so it cannot be changed. Other cases fall under the heading "creepy action at a distance" or perhaps the "fundamental language invariant."- No resources were missed, the program as a whole is still agreed.
In the move constructor:
Foo(Foo&& o);
basic warranty gives fewer warranties. o can be changed, because it is involved through a non-constant link, so it can be in any state.
Then view vector::resize . Its implementation, as a rule, will follow the same pattern:
void vector<T, A>::resize(std::size_t newSize) { if (newSize == size()) return; if (newSize < size()) makeSmaller(newSize); else if (newSize <= capacity()) makeBiggerSimple(newSize); else makeBiggerComplicated(newSize); } void vector<T, A>::makeBiggerComplicated(std::size_t newSize) { auto newMemory = allocateNewMemory(newSize); constructAdditionalElements(newMemory, size(), newSize); transferExistingElements(newMemory); replaceInternalBuffer(newMemory, newSize); }
The key function here is transferExistingElements . If we use only copying, it has a simple guarantee: it cannot change the original buffer. Therefore, if an operation is called at some point, we can simply destroy the newly created objects (keep in mind that the standard library absolutely cannot work with throwing destructors), discard a new buffer and reconstruct. The vector will look as if it had never been modified. This means that we have a strong guarantee, although the element instance constructor offers only a weak guarantee.
But if we use movement instead, this will not work. After moving one object, any subsequent exception means that the original buffer has been changed. And since we have no guarantee that moving objects will not rush back either, we cannot even recover. Thus, in order to maintain a strong guarantee, we must demand that the move operation not make any exceptions. If we have this, we are fine. And so we have move_if_noexcept .
Regarding the difference between MSVC and GCC: MSVC only supports noexcept from version 14, and since this is still under development, I suspect that the standard library has not been updated to take advantage.