4

I'm trying to nail down the differences between N3337 §8.5p7 (C++11) and N3797 §8.5p8 (post C++11) that deal with value-initialization.

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.

Given these two rules the snippet below should give different results:

#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';
}

as follows:

  1. According to N3337, the default constructor for Derived is called, as Derived has a user-provided constructor. The default constructor for Derived calls Base default constructor, which initializes Derived::i = 10, leaving Derived::j unitialized.
  2. From N3797, as Derived has no user-provided default constructor, nor a deleted default constructor, the second bullet point applies. That is, Derived is zero-initialized, i.e., both Derived::i and Derived::j are initialized with 0 and the object d is default-initialized, which leaves Derived::i = 10.

Although my knowledge of Unixes is minimum, I've been trying to replicate these two cases, using different flags for the compilers clang++ and g++, by trial and error, in Coliru, to no avail. The results so far, all printed d.i = 10 d.j = 0 without warnings.

4

1 回答 1

3

OP 中的程序无法区分d.j是初始化为 0 还是未初始化且恰好为 0。如果要在已初始化为已知非零值,例如放置新:

 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 在他的评论中所说,编译器通常会跟踪由于标准缺陷(而不是新功能)而导致的更改,并将它们包含在对特定标准修订版的支持中。鉴于标准不断变化,可能没有编译器可以完全编译任何给定标准文档中指定的语言。例如,当您告诉 clang 3.4 编译 C++11 时,它实际实现的语言是“C++11 的一部分加上我们在 3.4 版本中及时实现的相关缺陷解决方案(IIRC 全部用于 3.4) 。”

OP 询问的值初始化措辞的特定更改发生在核心工作组 (CWG) 缺陷报告 (DR) 编号 1301的决议中,该决议还涉及DR1324DR1368。作为缺陷解决方案,编译器将有理由实施更改。

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

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

于 2014-01-09T18:05:33.183 回答