I see some good discussion going on, thanks to everyone participating! Rest assured that I value both side's contributions, it is important for me (and those agreeing with my perspective) to be able to defend this position and also to keep looking for improvements. If our side can not be defended, it's probably not good enough :)
That said, here's what I hope can serve as a first summary, let's first look at it from a technical perspective:
If an operator returns an rvalue reference (T&&), it is safe for all use-cases except binding the result to an rvalue- or const-lvalue-reference:
T r = a + b + c; // no problem
const T r = a + b + c; // no problem
T& r = a + b + c; // compile-time error
const T& r = a + b + c; // problem (P1)
T&& r = a + b + c; // problem (P2)
It seems that this is consistent behavior across at least GCC, Clang and VC++. If a compiler shows a different behavior, its likely a compiler-bug.
Technically, there is no doubt that the above is a bug, but that doesn't answer the question of whether this is a bug of the user who wrote the above code or if it's a bug of the operator to return an rvalue reference instead of an rvalue. This is where we leave the technical-aspects-land and enter the minefield of opinions, expectations and decisions. I am of the opinion that the above problematic use-cases are a bug on the users side, my line-of-thoughts and my arguments are:
a) The above code is explicit, you can spot it in the users code. It's not something that happens silently.
b) The above code always looked suspicious to me anyways, it has a code-smell that I just don't like.
c) It rely's on the fact that it extends the lifetime of a returned temporary value. The rules of the C++ standard that allow this are quite clear, and hence it's the users responsibility to make sure that there actually is a temporary value whose lifetime can be extended. It has always been an error if the expression returned a reference and not a value. Consider
const std::string& oops = std::vectorstd::string(1, "hello, world" ).front();
This compiles are creates a dangling reference, as the standard says that front() returns a reference and not a value. It's not 100% comparable as in this case the standard guarantees a bug, but the main point for C++ has always been which guarantees are given and not about a certain expectation one might have. As an example:
static_assert( std::is_empty< std::tuple<> >::value, "Expectation 1 broken" );
static_assert( std::is_empty< std::array< T, 0 > >::value, "Expectation 2 broken" ); // with any valid T
The first case is certainly a reasonable expectation and it currently works for all implementations of std::tuple that I know of, but it is not guaranteed! If your code relies on it and it breaks in the future, you can not blame the person who modified std::tuple. The second case is also a reasonable expectation, but it breaks everywhere and AFAIK there is no standard-conforming way to implement std::array so that std::array