Does anybody know why this code crashes?
Code that crashes:
QList<int> lst;
const auto& tmp = QList<int>() << 1;
lst = tmp;
Code that works (tmp is not a reference):
QList<int> lst;
const auto tmp = QList<int>() << 1;
lst = tmp;
Compiler:
Apple LLVM version 4.2 (clang-425.0.28) (based on LLVM 3.2svn)
Target: x86_64-apple-darwin12.5.0
Thread model: posix
Crash message:
qt_test(76726,0x7fff76257180) malloc: *** error for object 0x101208b60: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
qt_test: line 43: 76726 Abort trap: 6 $DBG_TEST "$@"
This message appears when 'lst' destructor called.
Under VisualStudio 2012 this code works well.
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.