当前位置:  开发笔记 > 编程语言 > 正文

使用临时寿命差异

如何解决《使用临时寿命差异》经验,为你挑选了3个好方法。

我们正与朋友们就代码进行热烈的讨论:

#include 
#include 

using namespace std;

string getString() {
    return string("Hello, world!");
}

int main() {
    char const * str = getString().c_str();
    std::cout << str << "\n";
    return 0;
}

这段代码在g ++,clang和vc ++上产生不同的输出:

g++clang输出是一样的:

你好,世界!

但是vc++没有输出(或只是空格):

什么行为是正确的?根据临时生活,这可能是标准的变化吗?

据我所知,通过阅读IR clang++,它的工作原理如下:

store `getString()`'s return value in %1
std::cout << %1.c_str() << "\n";
destruct %1

就个人而言,我认为gcc也是这样的(我用rvo/move verbosity测试它(自定义ctors和dtors打印到std::cout).为什么vc ++以其他方式工作?

clang = Apple LLVM版本6.1.0(clang-602.0.53)(基于LLVM 3.6.0svn)

g ++ = gcc版本4.9.2(Debian 4.9.2-10)



1> Lightness Ra..:

您的程序有未定义的行为!你正在"打印"一个悬垂的指针.

getString()临时字符串的结果不再是该const char*声明; 因此也没有调用c_str()那个临时的结果.

所以两个编译器都是"正确的"; 这是你和你的朋友错了.

这就是为什么我们不存储结果std::string::c_str(),除非我们确实需要.


@VictorPolevoy这是未定义行为的"未定义"部分.:)

2> TartanLlama..:

两者都是正确的,未定义的行为未定义.

char const * str = getString().c_str();

getString()返回一个临时的,它将在包含它的完整表达式的末尾被销毁.因此,在该行完成之后,str是一个无效的指针并试图检查它将使你陷入未定义行为的土地.


一些标准报价(根据要求)(来自N4140):

[class.temporary]/3: 临时对象被破坏,作为评估(词法上)包含创建它们的点的完整表达式的最后一步.

basic_string::c_str 指定如下:

[string.accessors]/1:一个指针p,p + i == &operator[](i)每个iin [0,size()].

由于字符串的内容是连续存储的([string.require]/4),这实际上意味着"返回指向缓冲区起始处的指针".

显然当a std::string被破坏时,它将回收任何已分配的内存,使该指针无效(如果你的朋友不相信,他们还有其他问题).



3> 6502..:

这是未定义的行为,因此任何事情都可能发生(包括"正确"打印字符串).

无论如何,让事情"正常"发生在UB上,除非该程序实际上是在付费客户的计算机上运行,​​或者它是在大屏幕上的大屏幕上显示的;-)

问题是你在const char *使用指针之前指向一个被销毁的临时对象.

请注意,这是一样的情况,与:

const std::string& str = getString(); // Returns a temporary
std::cout << str << "\n";

因为在这种情况下,有一个非常具体的规则,关于绑定到C++标准中临时值的引用.在这种情况下,临时的生命周期将延长,直到参考str也被销毁.该规则只适用于引用且仅当直接绑定到临时或临时(像的子对象const std::string& s = getObj().s;),而不是调用一个临时对象的方法的结果.

推荐阅读
夏晶阳--艺术
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有