Template function which determines how to move-assign a type In some code that I am writing, I am manipulating some heterogeneous containers of user-defined types.

I desperately need to be able to move-assign these types without generating exceptions.

However, this can cripple the usability of my code because on many older compilers, important standard library types do not have noexcept markings on their move-assignment operators. For instance std::string appears not to be noexcept move-assignable with gcc 4.9, it is only noexcept move constructible in C++11.

To work around this, I have developed a template function which tries various strategies for move-assigning things until it finds one which is noexcept, if it cannot then it gives up.

namespace traits {

template &lt;bool b, typename V = void&gt;
using enable_if_t = typename std::enable_if&lt;b, V&gt;::type;

} // end namespace traits

namespace detail {

template &lt;typename T&gt;
auto move_assign_noexcept(T &amp; dest, T &amp;&amp; src) noexcept -&gt; traits::enable_if_t&lt;std::is_nothrow_move_assignable&lt;T&gt;::value&gt; {
  dest = std::move(src);
}

template &lt;typename T, typename ENABLE = void&gt;
struct is_nothrow_swappable : std::false_type {};

template &lt;typename T&gt;
struct is_nothrow_swappable&lt;T, traits::enable_if_t&lt;noexcept(std::swap(*static_cast&lt;T*&gt;(nullptr), *static_cast&lt;T*&gt;(nullptr)))&gt;&gt; : std::true_type {};

template &lt;typename T&gt;
auto move_assign_noexcept(T &amp; dest, T &amp;&amp; src) noexcept -&gt; traits::enable_if_t&lt;!std::is_nothrow_move_assignable&lt;T&gt;::value
                                                                   &amp;&amp; is_nothrow_swappable&lt;T&gt;::value&gt; {
  std::swap(dest, src);
}

template &lt;typename T&gt;
auto move_assign_noexcept(T &amp; dest, T &amp;&amp; src) noexcept -&gt; traits::enable_if_t&lt;!std::is_nothrow_move_assignable&lt;T&gt;::value
                                                                   &amp;&amp; !is_nothrow_swappable&lt;T&gt;::value
                                                                   &amp;&amp; std::is_nothrow_move_constructible&lt;T&gt;::value&gt; {
  dest.~T();
  new (&amp;dest) T(std::move(src));
}

} // end namespace detail


This code is supposed to work at C++11 standard.

I recall that there is a standard function std::move_if_noexcept which is useful for standard containers which are trying to avoid exception problems.

Is there an existing standard idiom for doing what I am trying to do?

Are there other strategies that I should try? Am I actually using standard traits correctly to ensure that each implementation actually won't throw?
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.