All cases are correct. They all will construct a temporary and apply the copy constructor of the return type. Necessarily, if there is no copy constructor, the code will fail.
RVO will happen on all three cases under most compilers. Only difference is the last one where the standard does not force it. This because you have a named variable. But most compilers are smart enough to apply RVO to it still... the later the named variable is declared and the less transformations it is applied to, the better odds for RVO to be applied to a named variable.
Incidentally, returning a reference is of course possible as you might have seen in other code. What you must not do is return a reference t a local object.
std::string& get_a_string2()
{
std::string str("hello");
return str; //error!
}
Will produce a compile time error, as you know. However,
std::string& get_a_string2(std::string& str)
{
// do something to str
return str; //OK
}
Will work just fine. On this case, there's no construction or copy construction involved. Simply the function returns a reference to its argument.