我有一个功能:
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
对象被销毁?
如果没有返回值优化,我认为会发生这种情况。 如果是这样会发生什么?
答案 0 :(得分:3)
该字符串直接在meat
中构建。没有具有不同生命的临时工。这被称为elision。
这种行为是在C ++ 17下强制执行的,并且在实践中发生在任何合理的现代生产质量的现代编译器中,在C ++ 03 11和14中没有设置病态构建标志。
在C ++ 14及更早版本中,类必须有一个移动或复制ctor才能实现上述功能,或者你得到一个构建中断。所述构造函数中的代码都不会运行。
古代或玩具编译器,或带有病态标志的编辑器告诉他们不要忽视,可能最多可以制作2个临时对象并乱用副本。这种情况并不重要,因为病态编译器状态同样可以自由地实现a+=b;
(a
和b
整数类型)for (i from 0 to b)++a;
!你应该诚实地认为缺乏同样的病态。
C ++中的Elision指的是标准允许的对象生存期和身份的合并。所以在某种意义上存在3个字符串(函数的临时eithin,返回值和从返回值构造的值),它们的身份被合并到一个具有统一生命期的对象中。
答案 1 :(得分:2)
您可以使用自定义结构对此进行测试:
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");
因此只调用const char *
中的构造函数。
现在,如果你告诉g ++不要忽略构造函数(-fno-elide-constructors
),那么有三个构造函数/析构函数调用:
S("S")
; get_s
,S(S&&)
; main
; get_s
返回的临时表的析构函数; s
的析构函数。如果S
没有移动构造函数,则可以简单地用上面列表中的复制构造函数替换移动构造函数。