一个trivial构造函数(或析构函数)和用户定义的空构造函数(或析构函数)有什么区别?

11
C++标准定义了当类具有平凡构造函数和/或平凡析构函数时的一些非常具体的行为。
例如,根据标准的§3.8/1:
一个类型为T的对象的生命周期在以下情况下结束: - 如果T是一个具有非平凡析构函数(12.4)的类类型,则析构函数调用开始,或 - 对象占用的存储被重用或释放。
因此,
- 如果一个对象不是平凡可销毁的,则在析构函数调用后尝试访问对象成员是未定义的行为。 - 如果一个对象是平凡可销毁的,则在析构函数调用后尝试访问对象成员是安全的,不会导致未定义的行为。
尽管这个例子可能不是最好的例子,但它显示出了一个对象是否是平凡可销毁的差异可能是关键的(UB / non-UB)。
标准的§12.4/3规定(总结):如果类T的析构函数是隐式定义的、非虚拟的,并且类T的所有基类和成员都是平凡销毁的,则T的析构函数是平凡的。
就我(谦虚)的经验而言,我从未看到过编译器生成的代码方面的区别,包括:
- 具有平凡默认构造函数和/或平凡析构函数的类,和 - 具有用户定义的空构造函数和/或非虚拟的用户定义的空析构函数的类(只要类、其基类和成员类也具有非虚拟析构函数用户定义的空或平凡)。
因此,我的问题是:
- 用户定义的空构造函数/析构函数在编译器代码生成、优化、权衡等方面如何被认为是类似于平凡构造函数/析构函数。 - 相同的问题与用户定义的非空构造函数/析构函数;一个在构造函数/析构函数中实现的代码应该遵循什么规则才能将它们视为类似于平凡的。
我的问题与标准无关(请不要回答标准规定了什么是平凡构造函数/析构函数,因此用户定义的构造函数/析构函数不是),而是与编译器处理用户定义的构造函数/析构函数的方式以及编译后代码的行为如何改变(或不改变)相关。
2个回答

2
你比我更了解这些标准,但根据你提供的信息,该标准定义了一个微不足道的析构函数,但它没有定义空析构函数,这会让这个问题有点误导。微不足道的析构函数是编译器可以进行优化的特殊情况,虽然对我们来说空构造函数是有意义的,但编译器编写者并不一定要考虑这种情况。
浏览一些SO链接: 回答你的第二个问题,只要你的构造函数不为空,它就不是平凡的。最接近平凡的情况是一个空的构造函数/析构函数,但是你对标准的仔细阅读已经告诉你这不被定义为平凡。
简而言之,标准定义了平凡的析构函数,但没有定义空析构函数。聪明的编译器可以选择注意到它是用户定义的空函数并将其视为平凡,但标准不要求任何这样的考虑。

1

关于编译器代码生成、优化、权衡等方面,用户定义的空构造函数/析构函数是否可以被视为类似于平凡构造函数/析构函数的构造函数/析构函数?

如果构造函数/析构函数没有被内联,则编译器可能(取决于链接时优化)必须调用它们,即使它们是无操作。

例如,以下代码:

struct Struct {
  Struct();
  ~Struct();
};

int main() {
  Struct s;
}

被编译成(启用优化):
main:
        subq    $24, %rsp
        leaq    15(%rsp), %rdi
        call    Struct::Struct()
        leaq    15(%rsp), %rdi
        call    Struct::~Struct()
        xorl    %eax, %eax
        addq    $24, %rsp
        ret

请注意,即使我可以将构造函数和析构函数定义为空函数并放在单独的文件中,仍然需要调用它们。
但是,如果您已经内联了这些定义:
struct Struct {
  Struct() {}
  ~Struct() {}
};

Struct foo() {
  return Struct{};
}

然后编译器可以(如果它不是完全糟糕的话)将它们视为普通的构造函数/析构函数:

foo():
        movq    %rdi, %rax
        ret

在该示例中,任何构造函数/析构函数调用都被完全优化掉了,生成的代码与如果 Struct 的定义是简单的 struct Struct {}; 相同。
对于具有用户定义的非空 ctor/dtor 的相同问题; 实现在 ctor/dtor 中的代码应遵循什么规则才能将它们视为类似于 trivial。
这有点取决于情况。同样,如果构造函数/析构函数没有内联,则编译器可能仍然必须对它们发出调用,这种情况下它们根本不是类似于 trivial。
但是,如果内联非空构造函数/析构函数可以完全被优化掉(例如,如果它们只包含 for (int x = 0; x < 1000; ++x);,那么这是无用的代码,可以被优化掉),那么它们仍然可能是 "类似于 trivial" 的。
但是,如果它们执行不能被优化掉的有用工作,那么它们就根本不是类似于 trivial。它们会被执行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接