有人知道为什么这段代码会崩溃吗?
崩溃的代码:
QList<int> lst;
const auto& tmp = QList<int>() << 1;
lst = tmp;
有效的代码(tmp不是引用):
QList<int> lst;
const auto tmp = QList<int>() << 1;
lst = tmp;
编译器:
Apple LLVM version 4.2 (clang-425.0.28) (based on LLVM 3.2svn)
Target: x86_64-apple-darwin12.5.0
Thread model: posix
崩溃讯息:
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 "$@"
当'lst'析构函数被调用时,会出现此消息。
在VisualStudio 2012下,此代码运行良好。
答案 0 :(得分:2)
在考虑了这个之后,我终于发现了明显的不一致。
QList<int> lst;
const auto& tmp = QList<int>() << 1;
lst = tmp;
具体来说,看看这一行:
const auto& tmp = QList<int>() << 1;
QList<int>()
是暂时的。此临时值将传递到QList<int>::operator<<(...)
,然后将其作为参考返回。这里的问题是间接的程度;如果你试图将QList<int>()
存储到const-reference中,它应该存在,直到const-reference超出范围。但是该引用反而作为this
传递给运算符,并在语句结束时丢失。
为什么这在Visual Studio中起作用而不是GCC是有问题的。一个快速谷歌的答案至少有一个interesting result,它指向标准中的以下条款:
如果初始化表达式是rvalue,T2是类类型,并且“cv1 T1”与“cv2 T2”引用兼容,则引用以下列方式之一绑定(选择是实现定义的):
引用绑定到rvalue表示的对象或带有该对象的子对象
创建一个类型为“cv1 T2”的临时表,并调用构造函数将整个右值对象复制到临时对象中。此引用绑定到临时或具有临时的子对象。
因此,看起来Visual Studio正在采用第二种方法,这恰好在这种边缘情况下有所不同,因为该值无论如何都将被复制到语句中。但它确实对const-reference的有用性提出了质疑。