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.

Casey
  • 41,449
  • 7
  • 95
  • 125
Wake up Brazil
  • 3,421
  • 12
  • 19
  • 3
    I have a very hard time reading a question that begins "Need someone to tell me" and not thinking "Read the damned manual on your own." Perhaps I'm jumping the gun, but I stopped reading there. Sometimes *how* you ask a question is more important than what question you're asking. – John Dibling Jan 09 '14 at 14:37
  • I have to agree with @JohnDibling that how you ask can make a difference, it might also help if you could document the different flags combination you attempted to use. – Shafik Yaghmour Jan 09 '14 at 14:40
  • Are you sure that the compilers you tried already implement the changes? – PlasmaHH Jan 09 '14 at 14:41
  • @ShafikYaghmour I've tried with g++ -std=c++11 and std=c++0x with different versions -4.8, -4.7, -4.6. – Wake up Brazil Jan 09 '14 at 14:44
  • @PlasmaHH The results I've obtained so far show that both compilers are already updated to N3797, which is post-C++11. – Wake up Brazil Jan 09 '14 at 14:45
  • 1
    There's no way for your program to determine if `d.j` is being initialized to 0 or if it's uninitialized and simply happens to be 0. You could tell if you create a `Derived` in memory that's already initialized, say with placement `new`: `Derived d{42}; ::new (&d) Derived{};` – Casey Jan 09 '14 at 16:14
  • The question essentially is, what does `-std=c++11` mean for clang++ and g++, and is there a way to force a mode that uses the origninal "bugged" Standard. I agree it's confusing, as different versions seem to implement a different (monotonically increasing) number of defect reports for their `-std=c++11` mode. – dyp Jan 09 '14 at 16:17
  • @Casey As I said I have almost no familiarity with Unixes, but I would expect a warning for a member being used (`cout << ...`), but yet unitialized. – Wake up Brazil Jan 09 '14 at 16:17
  • [The version of Visual C++ used at rextester.com follows the C++11 rule.](http://rextester.com/IMELZ32973 "Code sample at rextester.com") I believe `_MSC_VER = 1800` corresponds to MSVC2013. – Casey Jan 09 '14 at 16:20
  • @Dyp Exactly. That's why I tried with -std=c++11 and -std=c++0x. I would expect this last one to emit a warning. – Wake up Brazil Jan 09 '14 at 16:21
  • Hmm the [current gcc docs](http://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html#C-Dialect-Options) say this about `-std=c++11` **and** `-std=c++0x`: *The 2011 ISO C++ standard plus amendments.* – dyp Jan 09 '14 at 16:24
  • @Casey That's interesting, but MS compilers still don't compile `= default`and `= delete`. As a matter of fact I'd say that MS compilers don't support the great majority of clauses in N3337. – Wake up Brazil Jan 09 '14 at 16:25
  • @WakeupBrazil Ok, if you don't believe me maybe you'll believe [What's New for Visual C++ in Visual Studio 2013](http://msdn.microsoft.com/en-us/library/hh409293.aspx). – Casey Jan 09 '14 at 16:29
  • @Casey As a matter of fact I'd say that MS compilers don't support the great majority of clauses in N3337. I forgot to say that my compiler is VS2010!!. That might explain it. – Wake up Brazil Jan 09 '14 at 16:31
  • @dyp Does that mean the -std=c++11 and -std=c++0x are equivalent? – Wake up Brazil Jan 09 '14 at 16:36
  • @WakeupBrazil It seems so. – dyp Jan 09 '14 at 16:39
  • @Casey See my snippet in rextester (http://rextester.com/EGPT1431). It clearly shows that `d.j` is unitialized. It would be nice if I had a MS compiler supporting N3797 to clear up everything. Thanks. – Wake up Brazil Jan 09 '14 at 16:49
  • FYI: This wording change happened in [the resolution of CWG DR 1301](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1301) which was accepted at the February 2012 committee meeting. (See also [DR1324](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1324), and [DR1368](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1368).) Changes due to defects (as opposed to new features) are typically incorporated by compilers. – Casey Jan 09 '14 at 17:29
  • @Casey Before I read the last things you submitted above, take a look at this (http://coliru.stacked-crooked.com/a/3019f830132572db). If you change the flags to -4.6 and -std=c++0x you get the unitialized result. So you're right about what you said in an earlier comment. I'll accept that as an answer. A very good answer, indeed. Ths. – Wake up Brazil Jan 09 '14 at 17:33

1 Answers1

3

The program in the OP cannot distinguish if d.j is being initialized to 0 or if it is uninitialized and coincidentally happens to be 0. This would be clear if the Derived object in question were to be created in memory that is already initialized to a known non-zero value, say with placement new:

 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.

As dyp says in his comment, compilers typically track changes due to defects in the standard (as opposed to new features) and include them in their support for a particular standard revision. There is likely no compiler that compiles exactly the language as specified in any given standard document given that the standards are constantly in flux. When you tell, e.g., clang 3.4 to compile C++11 the language it actually implements is "the portion of C++11 plus pertinent defect resolutions that we had implemented (IIRC all of it for 3.4) in time for the 3.4 release."

The particular change to the value-initialization wording that the OP asks about happened in the resolution of Core Working Group (CWG) Defect Report (DR) number 1301 which also addressed DR1324 and DR1368. As a defect resolution, compilers would then have reason to implement the change.

Analysis with various compilers and versions (mostly performed by the OP) demonstrates:

In summary, there's no way to force a compiler to perform exactly as specified, but we can usually determine what's going on anyway with some careful analysis.

Community
  • 1
  • 1
Casey
  • 41,449
  • 7
  • 95
  • 125