#include <iostream>
#include <type_traits>
using namespace std;

namespace estd {

template<class T, class U = T, std::enable_if_t<
        std::is_move_constructible<std::remove_reference_t<T>>::value && 
        //std::is_assignable<std::remove_reference_t<T>, std::remove_reference_t<U>&&>::value && 
        !std::is_lvalue_reference<U>::value,
    int> = 0    
>
T exchange(T& obj, U&& new_value) noexcept(
    std::is_nothrow_move_constructible<std::remove_reference_t<T>>::value &&
    std::is_nothrow_assignable<std::remove_reference_t<T>, std::remove_reference_t<U>&&>::value
)
{
    T old_value {std::move(obj)};
    obj = std::move(new_value);
    return old_value;
}

} // namespace estd
void do_stuff() noexcept
{ /*...*/ }

class Sample
{
    std::string mBody;
public:
    Sample(const std::string& body = ""): mBody {body} {}
    
    Sample(const Sample& s): mBody {s.mBody} {
    	printf("%s\n", __PRETTY_FUNCTION__); // noexcept
    }
    
    Sample& operator=(const Sample& s) { 
    	mBody = s.mBody; 
    	printf("%s\n", __PRETTY_FUNCTION__); // noexcept
    	return *this;
    }
    
    Sample(Sample&& dying) noexcept(
        noexcept(do_stuff()) && 
        noexcept(estd::exchange(dying.mBody, {}))
    ):
        mBody {estd::exchange(dying.mBody, {})}
    {
        do_stuff(); // noexcept
    	printf("%s\n", __PRETTY_FUNCTION__); // noexcept
    }
    
    Sample& operator=(Sample&& dying) noexcept(
        noexcept(do_stuff()) && 
        noexcept(estd::exchange(dying.mBody, {}))
    )
    {
    	mBody = estd::exchange(dying.mBody, {});
    	do_stuff();
    	printf("%s\n", __PRETTY_FUNCTION__); // noexcept
    	return *this;
    }

    std::string body() const noexcept {return mBody;}
};

int main()
{
    std::cout << std::boolalpha;
    Sample rval{"wow such string very content"};
    Sample dummy;
    std::cout << noexcept( Sample(std::move(rval)) ) << std::endl; // prints true
    std::cout << noexcept( dummy = std::move(rval) ) << std::endl; // prints true

    // The rest only to show that move semantics actually work
    Sample f (std::move(rval));             // Calls move ctor
    std::cout << rval.body() << std::endl;  // prints empty line, empty string moved in rval
    std::cout << f.body() << std::endl;     // prints wow such string very content
    
    // estd::exchange(f, rval); // Fails to compile bc rval is an lvalue reference, template disabled
    std::cout << (estd::exchange(f, std::move(rval))).body() << std::endl; // Ok, calls move ctor and move assignment (in estd::exchange) and prints wow such string very content
    std::cout << f.body() << std::endl; // empty line, rval (empty) moved in f
    std::cout << "end" << std::endl;
}