C++ 这里的具体操作顺序是什么?
我有一个功能:C++ 这里的具体操作顺序是什么?,c++,c++11,C++,C++11,我有一个功能: std::string makeMeat() { return "Pork"; } 在代码的某个地方,我这样使用它: std::string meat = makeMeat(); 我想知道在这行代码上进行的操作的确切顺序是什么。假设两种不同的情况: string没有移动构造函数(例如) string具有move构造函数 我猜makeMeat()创建类std::string的临时对象 std::string temp("Pork"); 在这之后,std::string mea
std::string makeMeat() { return "Pork"; }
在代码的某个地方,我这样使用它:
std::string meat = makeMeat();
我想知道在这行代码上进行的操作的确切顺序是什么。假设两种不同的情况:
makeMeat()
创建类std::string
的临时对象
std::string temp("Pork");
在这之后,std::string meat
对象由temp
对象中的数据创建并用复制构造函数初始化
std::string meat(temp);
最后,temp
对象被销毁了吗
我认为如果没有回报值优化,这种情况就会发生。
如果是,会发生什么?字符串直接在
meat
中构造。不存在具有不同生命周期的临时变量。这就是所谓的省略
这种行为在C++17下是强制性的,实际上,在任何合理的现代生产质量的现代编译器中都会发生,在C++03 11和14中没有设置病态的构建标志
在C++14及更早版本中,类必须有一个move或copy-ctor才能执行上述操作,否则将导致生成中断。所述构造函数中的任何代码都不会运行
古代或玩具编译器,或带有病态标志的编译器告诉他们不要省略,可能会组成2个临时对象,并与副本混在一起。这种情况并不有趣,因为病理编译器状态同样可以自由实现a+=b代码>(具有a
和b
积分类型)为for(i从0到b)+a代码>!你应该诚实地认为缺乏精明同样是病态的。
C++中的Eclipse是指对象生命周期和身份的标准允许合并。因此,在某种意义上,存在3个字符串(函数中的临时EIT、返回值和由返回值构造的值),它们的标识被合并到一个具有统一生存期的对象中。
您可以使用自定义结构对此进行测试:
struct S {
S (const char *);
S (S const&) = default;
S (S&&) = default;
virtual ~S();
};
S get_s () { return "S"; }
int main () {
S s = get_s();
}
如果没有选项,g++将省略大多数构造函数调用,此代码相当于:
S s("S");
因此,只调用constchar*
中的构造函数
现在,如果您告诉g++不要elide构造函数(-fno elide构造函数
),则有三个构造函数/析构函数调用:
第一个创建一个临时的S(“S”)
李>
第二种方法是在get_s
,s(s&&)
内部创建一个临时的李>
然后调用第一个临时变量的析构函数李>
然后在main
内部调用move构造函数李>
然后调用get\u s
返回的临时文件的析构函数李>
然后调用s
的析构函数
如果S
没有移动构造函数,您可以简单地用上面列表中的复制构造函数替换移动构造函数。这对于您自己的测试非常简单。您不用使用std::string
而是使用所需的构造函数和函数创建自己的类,这些构造函数和函数在调用时打印消息。然后你可以自己看看会发生什么。@JoachimPileborg在使用复制省略之类的功能时,我会说“自己试试”确实会给你一个答案,但不是一个反映所有必须发生的事情、可能发生的事情、需要可用的事情等的答案。不确定,但是:“Pork”
不是类型为std::string
的PR值,因此,编译器不需要在C++17中执行复制省略。@holt,但我们构造了一个类型为std::string
的prvalue,从“Pork”
返回,返回的是,否?不是绝对确定,但如果不是真的,我会很失望。@Holt:编译器从来都不是执行复制省略的“必需”编译器。只有“允许”这样做。@Nawaz我们说的是C++17,其中一些复制省略是必须的。@holt和我认为C到B也是如此。我应该再检查一遍。现在阅读…你能再解释一下第二步吗?它是否创建了一个实际“返回”到主流的对象?@vector2718在内存中有三个地方分配了S
对象的存储:a)在主函数中,B)在get\u S
的“调用堆栈”中,C)在get\u S
的堆栈中。对象S(“S”)
首先在C
中构造,然后在返回位置B
中移动,然后在其目标位置A
中移动。我理解C)是get_S的堆栈框架,它存储此函数的所有参数和局部变量。但是你所说的get_s的“调用堆栈”是什么意思?@vector2718“调用堆栈”在这里可能是一个不好的术语-当你有一个返回某个东西的函数时,你需要将返回值存储在某个地方。根据调用约定,您可以将其存储在寄存器(例如使用\uu stdcall
)或内存中的某个位置(在“调用堆栈”中)。