gpt4 book ai didi

c++ - 编译器生成的默认构造函数如何比自己编写的只初始化成员的构造函数更有效?

转载 作者:行者123 更新时间:2023-12-03 10:03:33 24 4
gpt4 key购买 nike

this answer 触发我正在阅读core guidelines :

C.45: Don’t define a default constructor that only initializes datamembers; use in-class member initializers instead


给出的理由是

Reason

Using in-class member initializers lets the compiler generate thefunction for you. The compiler-generated function can be moreefficient.


请注意,这特别是关于一个默认构造函数,它除了初始化成员之外什么都不做,指南建议一个 不应该写一个这样的构造函数。
“坏”的例子是:
Example, bad

class X1 { // BAD: doesn't use member initializers
string s;
int i;
public:
X1() :s{"default"}, i{1} { }
// ...
};

“好”的例子是使用类内成员初始值设定项并且没有用户声明的构造函数:
Example

class X2 {
string s = "default";
int i = 1;
public:
// use compiler-generated default constructor
// ...
};

在该特定示例(或任何其他示例)中,编译器生成的构造函数可以比用户提供的构造函数更有效地做什么?
初始值设定项列表是否没有提供与类内初始值设定项相同的优化机会?

最佳答案

简答
一个 default ed 构造函数应该具有与等效初始化构造函数相同的生成程序集 前提是作者包含正确的 constexprnoexcept状态 .
我怀疑“可以更高效”是指这样一个事实,即,一般来说,它会生成比同等的开发人员编写的代码更优化的代码,而后者会错过诸如 inline 之类的机会。 , constexpr , 和 noexcept .
长答案default的一个重要特征ed 构造函数执行的是他们解释并推导出 constexpr 的正确状态和 noexcept这是许多 C++ 开发人员未指定或可能未正确指定的内容。由于核心指南针对新老 C++ 开发人员,这可能就是提到“优化”的原因。constexprnoexcept状态可能会以不同的方式影响代码生成:

  • constexpr构造函数确保从常量表达式产生的值调用构造函数也将产生常量表达式。这可以允许诸如 static 之类的事情不是常量的值实际上不需要构造函数调用(例如,不需要静态初始化开销或锁定)。 注:这适用于本身不能存在于 constexpr 中的类型。上下文——只要constexpr构造函数的格式是良构的。
  • noexcept可能会生成更好的消费代码汇编,因为编译器可能会假设不会发生异常(因此不需要堆栈展开代码)。此外,实用程序,例如检查 std::is_nothrow_constructible... 的模板可能会生成更优化的代码路径。

  • 除此之外, default在类主体中定义的 ed 构造函数也使它们的定义对调用者可见——这允许更好的内联(同样,否则可能会错过优化的机会)。

    核心指南中的示例并没有很好地展示这些优化。但是,请考虑以下示例,该示例说明了可以从 default 中受益的实际示例。 ing:
    class Foo {
    int a;
    std::unique_ptr<int> b;
    public:
    Foo() : a{42}, b{nullptr}{}
    };
    在此示例中,以下情况为真:
  • Foo{}的构造是 不是 常量表达式
  • 施工Foo{}不是 noexcept

  • 对比一下:
    class Foo {
    int a = 42;
    std::unique_ptr<int> b = nullptr;
    public:
    Foo() = default;
    };
    从表面上看,这似乎是一样的。但突然之间,现在发生了以下变化:
  • Foo{}constexpr , 因为 std::unique_ptr std::nullptr_t constructorconstexpr (即使 std::unique_ptr 不能用在完整的常量表达式中)
  • Foo{}noexcept表达式

  • 您可以将生成的程序集与此 Live Example 进行比较.请注意 default case 不需要任何指令来初始化 foo ;相反,它只是通过编译器指令将值分配为常量(即使值不是常量)。
    当然,也可以这样写:
    class Foo {
    int a;
    std::unique_ptr<int> b;
    public:
    constexpr Foo() noexcept :a{42}, b{nullptr};
    };
    然而,这需要先验知识 Foo可以两者兼而有之 constexprnoexcept .弄错了会导致问题。更糟糕的是,随着代码的发展, constexpr/ noexcept状态可能会变得不正确——这就是 default ing 构造函数会被捕获。
    使用 default还有一个额外的好处是,随着代码的发展,它可能会添加 constexpr/ noexcept在可能的情况下——例如当标准库添加更多时 constexpr支持。最后一点对于作者来说每次代码更改时都需要手动处理。

    琐碎
    如果您取消使用类内成员初始值设定项,那么最后值得一提的一点是:在代码中没有办法实现平凡,除非它是由编译器生成的(例如通过 default ed 构造函数)。
    class Bar {
    int a;
    public:
    Bar() = default; // Bar{} is trivial!
    };
    Triviality 为潜在的优化提供了一个完全不同的方向,因为一个简单的默认构造函数不需要对编译器进行任何操作。这允许编译器省略任何 Bar{}完全如果它看到对象后来被覆盖。

    关于c++ - 编译器生成的默认构造函数如何比自己编写的只初始化成员的构造函数更有效?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56272431/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com