Template function which determines how to move-assign a type
I think you're putting the cart before the horse here.


  I am manipulating some heterogeneous containers of user-defined types.
  I desperately need to be able to move-assign these types without generating exceptions.


Let's say your container is a Cart&lt;T&gt;. And you need to define a move-assignment operator

Cart&lt;T&gt;&amp; Cart&lt;T&gt;::operator=(Cart&lt;T&gt;&amp;&amp; rhs) noexcept;


Well, if Cart behaves like std::vector, then you could implement this as something like

Cart&lt;T&gt;&amp; Cart&lt;T&gt;::operator=(Cart&lt;T&gt;&amp;&amp; rhs) noexcept
{
    delete [] this-&gt;pointer_to_Ts;
    this-&gt;pointer_to_Ts = rhs-&gt;pointer_to_Ts;
    rhs-&gt;pointer_to_Ts = nullptr;
}


Notice that this does not require T's own move-assignment operator at all; T might not even be movable! The only user-defined function we're executing here is T's destructor (and if that throws, we're in trouble anyway).

But okay, let's suppose that you're implementing Cart's move-assignment operator like this instead:

Cart&lt;T&gt;&amp; Cart&lt;T&gt;::operator=(Cart&lt;T&gt;&amp;&amp; rhs) noexcept
{
    for (int i=0; i &lt; n; ++i) {
        this-&gt;array_of_Ts[i] = std::move(rhs-&gt;array_of_Ts[i]);
    }
}


So now you're calling a ton of user-defined functions (well, just one function, but you're calling it n times). This is already violating the spirit of move-semantics, in that moving is supposed to be a fast operation, but we'll let that slide.

What should you do if T's move-assignment operator is defined as noexcept? Well, you're golden. Everything will work fine.

What should you do if T's move-assignment operator is not defined as noexcept, but doesn't throw in practice? Well, you're golden. Everything will work fine in practice. If it ever accidentally does throw an exception, then your program will call std::terminate and (presumably) exit. This seems like a good idea in most cases. If you really can't afford to call std::terminate when something as terrible as "move-assignment throws an exception" happens, then I bet you're coding to some strict government standards that will preclude the existence of non-noexcept move-assignment operators in the first place.

What should you do if T's move-assignment operator is explicitly defined as noexcept(false)? Well, you should probably go find the person who wrote that code and shoot them in the face. (Politely, of course.)



Now the unfortunate bit: It's actually impossible to tell in C++14 (and C++17 as well, AFAIK) whether a given function has been declared noexcept(false) or merely non-noexcept. So your code won't ever be able to distinguish case 2 above ("you're golden, in practice") from case 3 above ("you have to shoot somebody in the face") — it's useless to try.

So, should you go gray worrying about case 3? IMHO you should not. Just assume that every instance of case-2-or-3 in your codebase is really a case-2; and then write your code accordingly. It'll be simple and straightforward and efficient, and if you ever run into a throwing move-assignment operator, well, by definition it's the user who wrote the bad code, not you.



This is not the philosophy that led to the current pessimal behavior of std::vector::resize. I'll let someone else take a stab at explaining that philosophy, because I don't understand it at all.