After thinking about this more, I finally noticed the glaring inconsistency.
QList<int> lst;
const auto& tmp = QList<int>() << 1;
lst = tmp;
Specifically, looking at this line:
const auto& tmp = QList<int>() << 1;
QList<int>()
is a temporary. This temporary is being passed into QList<int>::operator<<(...)
, which is then returning it back as a reference. The problem here is the level of indirection; if you tried to store QList<int>()
into a const-reference, it should live until the const-reference fell out of scope. But that reference was instead passed to an operator as this
, and is lost by the end of the statement.
Why this works in Visual Studio and not GCC is questionable. A quick Google for answers turns up at least one interesting result where it points to the following clauses in the standard:
If the initializer expression is an rvalue, with T2 a class type, and “cv1 T1″ is reference-compatible with “cv2 T2,” the reference is bound in one of the following ways (the choice is implementation defined):
The reference is bound to the object represented by the rvalue or to a subobject with that object
A temporary of type “cv1 T2″ is created, and a constructor is called to copy the entire rvalue object into the temporary. This reference is bound to the temporary or to a subobject with the temporary.
So, it looks like Visual Studio is taking the second approach, which happens to make a difference in this edge case, since the value will be copied in the statement anyways. It really does put into question the usefulness of a const-reference, though.