使用暂时的寿命差异

Using of temporary lifetime difference

本文关键字:      更新时间:2023-10-16

我们正在和我的朋友就代码进行激烈的讨论:

#include <iostream>
#include <string>
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 详细程度对其进行了测试(打印到 std::cout 的自定义 ctor 和 dtor(。为什么 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(

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

getString() 的结果,一个临时字符串,不比该const char*声明长;因此,在该临时字符串上调用c_str()的结果也不会。

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

这就是为什么我们不会存储std::string::c_str()的结果,除非我们真的,真的需要。

两者都

是对的,未定义的行为是未定义的。

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

getString() 返回一个临时变量,该临时表达式将在包含它的完整表达式末尾销毁。因此,在该行完成后,str是一个无效的指针,试图检查它会让你陷入不确定行为的境地。


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

[class.temporary]/3: 临时对象在计算(词法上(包含创建它们的点的完整表达式的最后一步被销毁。

basic_string::c_str指定如下:

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

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

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

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

无论如何,UB经常使事情"正常工作",除非该程序实际上在付费客户的计算机上运行,或者如果它显示在大屏幕上,在广大观众面前;-(

问题在于,您正在获取一个临时对象内部的const char *,该对象在使用指针之前被销毁。

请注意,这种情况与以下情况不同

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

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

相关文章:
  • 没有找到相关文章