我正在尝试了解有关引用和rvalues的确切C ++(前C ++ 0x)行为。以下是否有效?
void someFunc ( const MyType & val ) {
//do a lot of stuff
doSthWithValue ( val);
}
MyType creatorFunc ( ) {
return MyType ( "some initialization value" );
}
int main () {
someFunc ( creatorFunc() );
return 0;
}
我在我试图修改的库中找到了类似的代码。我发现Visual Studio 2005下的代码崩溃了
我理解上述的方式,发生的是:
creatorFunc按值返回,因此创建了一个临时的MyType对象obj1。 (并保持......在堆栈上?)
someFunc正在引用该临时对象,并且随着计算的进行,临时对象被覆盖/释放。
现在,令人难以置信的是代码最常使用。更重要的是,只需一段简单的代码就无法重现崩溃。
这里发生了什么/应该发生什么?如果引用(val)是const,那有关系吗?从creatorFunc返回的对象的生命周期是什么?
答案 0 :(得分:4)
返回值具有临时的生命周期。在C ++中,这意味着创建该类型的完整表达式,因此在调用someFunc返回之前,不应调用MyType的析构函数。
我很好奇你的'被覆盖/被释放'。当然在这个对象上调用delete是不行的;它存在于堆栈中并删除它可能会导致堆损坏。此外,覆盖/修改它也可能是坏事。您的示例使用常量“C字符串”;在许多编译器上,这样的值存储在只读内存中,因此稍后尝试修改它可能会导致崩溃/访问冲突。 (但我不确定Visual C ++是否会进行此优化)。
通过const传递临时值与可变引用之间存在很大差异。标准C ++不允许创建对临时文件的可变引用,并且大多数编译器(包括GCC)都会拒绝它,尽管至少某些版本的Visual C ++允许它。
如果您使用可变引用传递它,您要写的是:
MyType t = creatorFunc();
someFunc(t);
答案 1 :(得分:3)
creatorFunc()
创建的临时文件的生命周期一直持续到someFunc
调用结束。实际上,下一个序列点(松散地,分号)。
someFunc正在引用该临时对象,随着计算的进行,临时对象被覆盖/释放“
听起来someFunc
正在做“坏事”。您不应该覆盖或释放由const&
传递的对象。
答案 2 :(得分:1)
通常,临时对象(例如函数调用返回的对象)的生命周期延伸到“封闭表达式”的末尾。但是,对引用的临时绑定通常会将其生命周期“提升”到引用的生命周期。
因此传递给someFunc()
的临时值应该保持活动状态并且可以在someFunc()
返回之后访问。但是,如果someFunc()
(或其可能调用的任何内容,例如doSthWithValue()
)将指针或引用存储到val
,例如在集合或其他对象中,则用于某些稍后,临时对象将不再存在,您可能会在尝试使用它时崩溃。这可能就是为什么你看到简单的代码没有崩溃,引用一个临时的,而更复杂的代码崩溃。
有关详细信息,请参阅以下项目:
return value (not a reference) from the function, bound to a const reference in the calling function; how is its lifetime extended to the scope of the calling function? C++ constant reference lifetime (container adaptor)
另请注意,该标准允许temporaries仅绑定到const引用,但正如Jack Lloyd提到的某些版本的MSVC(根据http://msdn.microsoft.com/en-us/library/cfbk5ddc.aspx在VS 2003之前)允许您将它们绑定到非const引用。但是,在快速测试中,我发现VS 2010的编译器仍然允许临时值绑定到非const引用。
答案 3 :(得分:0)
您只能将临时绑定到const引用。
答案 4 :(得分:0)
是的,重要的是引用是const。您不能将临时绑定到非const引用,因此代码将无法编译。
除此之外,您的代码定义明确,并且可以按预期工作。
答案 5 :(得分:0)
我遇到了同样的问题,当我在SO上发表评论的时候因为之前提到它而受到重创。我觉得其他人有这个问题的证据!!
我还在使用VS 2005(SP1),也许还有一些编译器优化错误?
来自creatorFunc()
的结果似乎没有为调用someFunc ( creatorFunc() );
保持活着,我发现唯一要做的就是将其拆分为2行,从而删除临时变量。