c++ - 如何调用 clang++ 或 g++ 来准确复制两个不同标准版本中的需求

标签 c++ c++11 compiler-warnings language-lawyer value-initialization

我试图确定 N3337 §8.5p7 (C++11) 和 N3797 §8.5p8(后 C++11)之间处理值初始化的差异。

N3337 §8.5p7:

To value-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
  • if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is called.
  • if T is an array type, then each element is value-initialized;
  • otherwise, the object is zero-initialized.

An object that is value-initialized is deemed to be constructed and thus subject to provisions of this International Standard applying to “constructed” objects, objects “for which the constructor has completed,” etc., even if no constructor is invoked for the object’s initialization.

N3797 §8.5p8:

To value-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9) with either no default constructor (12.1) or a default constructor that is user-provided or deleted, then the object is default-initialized;
  • if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T has a non-trivial default constructor, the object is default-initialized;
  • if T is an array type, then each element is value-initialized;
  • otherwise, the object is zero-initialized.

An object that is value-initialized is deemed to be constructed and thus subject to provisions of this International Standard applying to “constructed” objects, objects “for which the constructor has completed,” etc., even if no constructor is invoked for the object’s initialization.

鉴于这两条规则,下面的代码片段应该会给出不同的结果:

#include <iostream>
struct Base {
    int i;

    Base(int i):i(i) {}
    Base():i(10) {}
};

struct Derived : public Base {
    int j;

    Derived(int j):Base(j), j(j) {}
    Derived()=default;
};

int main() {
    Derived d{};
    std::cout << "d.i = " << d.i << "  " << "d.j = " << d.j << '\n';
}

如下:

  1. 根据 N3337,调用了 Derived 的默认构造函数,因为 Derived 具有用户提供的构造函数。 Derived 的默认构造函数调用 Base 默认构造函数,它初始化 Derived::i = 10,留下 Derived::j单元化。
  2. 从 N3797 开始,由于 Derived 没有用户提供的默认构造函数,也没有删除的默认构造函数,因此适用第二个要点。也就是说,Derived 是零初始化的,即 Derived::iDerived::j 都是用 0 初始化的,对象 d 是默认初始化的,它留下 Derived::i = 10

虽然我对 Unix 的了解很少,但我一直在尝试复制这两个案例,对编译器 clang++ 和 g++ 使用不同的标志,通过反复试验,在 Coliru 中,无济于事。到目前为止的结果,全部打印d.i = 10 d.j = 0 没有警告

最佳答案

OP 中的程序无法区分 d.j 是否被初始化为 0,或者它是否未初始化且恰好为 0。如果要创建有问题的 Derived 对象,这将很清楚在已经初始化为已知非零值的内存中,比如放置新的:

 Derived d{42};        // d.i and d.j are both 42.
 ::new (&d) Derived{}; // d.i is 0, d.j is 0 per N3797 or 42 per N3337.

作为dyp says in his comment ,编译器通常会跟踪由于标准中的缺陷(而不是新功能)引起的更改,并将它们包含在对特定标准修订版的支持中。鉴于标准不断变化,可能没有编译器可以完全编译任何给定标准文档中指定的语言。例如,当您告诉 clang 3.4 编译 C++11 时,它实际实现的语言是“C++11 的一部分加上我们为 3.4 版本及时实现的相关缺陷解决方案(IIRC 3.4 的所有部分)” ”

OP 询问的对值初始化 措辞的特定更改发生在 Core Working Group (CWG) Defect Report (DR) number 1301 的决议中。这也解决了DR1324DR1368 .作为缺陷解决方案,编译器随后将有理由实现更改。

各种编译器和版本的分析(主要由 OP 执行)表明:

总而言之,没有办法强制编译器完全按照指定的方式执行,但我们通常可以通过一些仔细的分析来确定到底发生了什么。

关于c++ - 如何调用 clang++ 或 g++ 来准确复制两个不同标准版本中的需求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21023071/

相关文章:

c++ - 防止 C++ OTL、DTL 或 SOCI 库中的 SQL 注入(inject)

c++ - std::uniform_real_distribution<double>(0,1) 能否返回大于 0.99999999999999994 的值?

c++ - 重载前模板实例化错误

c++ - 如何为 do {} while(false) 禁用 C4127

kotlin - IntelliJ 编译器关于为 varargs 传播空数组的警告

c++ - 您如何让cin只接受用户输入的数字?

c++ - Qt:typeid替代

c++ - 为什么专用模板函数不能同时接受类型及其常量版本?

java - Android Studio 中的原始类型警告

C++ 库包括矩阵的伪逆?