简体   繁体   中英

Const member stack vs heap

If I try to compile this code

struct A {
    const int j;
};
A a;

I'll get an expected error:

error: uninitialized const member in 'struct A'

but, if I try to compile this one :

struct A {
    const int j;
};
A * a = new A();

I'll get a successful build.

The question is: why does new allocation allows creating a variable with const member without explicit constructor and stack allocation - doesn't ?

It's not because of the heap allocation, but because of the parenthesis you use when allocating. If you do eg

A* a = new A;

it would also fail.

The reason it works when you add the parenthesis is because then your structure is value initialized, and for a POD-type like A value-initialization value-initializes each member, and the default value-initialization for int is zero.

That means it would probably work creating the variable on the stack as well, if you just add the value-initialization parenthesis:

A a = A(); // watch out for the http://en.wikipedia.org/wiki/Most_vexing_parse

Though that brings other potential problems, better use uniform initialization if you can (requies C++11):

A a{};

This is ill-formed as of C++14. §12.1 [class.ctor] says that

4 A defaulted default constructor for class X is defined as deleted if:

  • [...]
  • any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or-equal-initializer does not have a user-provided default constructor,
  • [...]

A default constructor is trivial if it is not user-provided and if:

  • its class has no virtual functions (10.3) and no virtual base classes (10.1), and
  • no non-static data member of its class has a brace-or-equal-initializer , and
  • all the direct base classes of its class have trivial default constructors, and
  • for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor.

§8.5 [dcl.init] says in turn that

7 To default-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9), the default constructor (12.1) for T is called (and the initialization is ill-formed if T has no default constructor or overload resolution (13.3) results in an ambiguity or in a function that is deleted or inaccessible from the context of the initialization);
  • [...]

8 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;
  • [...]

The empty pair of parentheses results in value-initialization. The default constructor for A is defined as deleted per [class.ctor]/p4. Thus, by [dcl.init]/p8, value-initialization means default-initialization, and by p7 the initialization is ill-formed because the constructor is deleted.

The C++11 version actually allowed value-initialization in this context; it says for value-initialization that, among other things,

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.

Since by the definition above the default constructor is trivial (even though it is deleted), it is not actually called. This was considered a defect in the standard and the relevant wording was changed by CWG issue 1301 .

Compiler vendors usually do implement defect resolutions, so this could be considered a bug in GCC 4.8 that's been fixed in 4.9.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM