Do I really need to implement user-provided constructor for const objects?

Solution 1:

N3797 §8.5/7 says:

If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.

There's no further example or explanation of this. I agree it seems pretty bizarre. Furthermore the rule was updated in C++11 to be more restrictive than it was in C++03, when class types needed user-declared constructors. (Your constructor is user-declared.)

The workaround is be to ask for value initialization using {}, or use Dietmar's clever out-of-class inline definition.

GCC does provide a diagnosis (and quite a nice one, referring to the newer C++11 requirements) if you add another member without an initializer.

  private:
    int i = 1;
    int j;

 

unmem.cpp:11:11: error: uninitialized const ‘a’ [-fpermissive]
   const A a;
           ^
unmem.cpp:1:7: note: ‘const class A’ has no user-provided default constructor
 class A {
       ^
unmem.cpp:3:5: note: constructor is not user-provided because it is explicitly defaulted in the class body
     A() = default;
     ^
unmem.cpp:7:9: note: and the implicitly-defined constructor does not initialize ‘int A::j’
     int j;

The GCC source refers to DR 253, Why must empty or fully-initialized const objects be initialized? This is an open issue in the standard, last updated in August 2011 (post-C++11) with this note:

If the implicit default constructor initializes all subobjects, no initializer should be required.

Therefore whereas Clang complies with C++11 (and will comply as-is with C++14), GCC is implementing the latest thinking of the standardization committee.

Filed a GCC bug. I predict that you'll need -pedantic to get a diagnosis when (and if) the bug is fixed.

Solution 2:

Note that you can turn your class easily into one which has a user-defined default constructor:

class A {
  public:
    A();

  private:
    int i = 1;
};

inline A::A() = default;

According to 8.4.2 [dcl.fct.def.default] paragraph 4:

... A special member function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. ...

This implicitly states that a function which is not explicitly defaulted on its first declaration is not user-provided. In combination with 8.5 [dcl.init] paragraph 6

... If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.

it seems clear that you cannot use a default constructor defaulted on its first declaration to initialize a const object. However, you can use a defaulted definition if it isn't the first declaration as is done in the code above.

Solution 3:

Edit: The following is based on outdated information. I just went through N3797 and this is what I found:

§ 8.5/7 [dcl.init]
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.

Note the standard quote in the link below says user-declared.


The following program compiles in g++ but not clang++:

struct A {};

void f()
{
  A const a;
}

And it might be related to this bug report where it was "fixed". g++ fails to compile it once it contains data members unless they're initialized. Note that int member = 1 will no longer make A a POD. Comparatively, clang++ rejects all permutations (empty classes and data members initialized or not.) For an interpretation of what the standard means by the following paragraph:

§ 8.5/9 [dcl.init] says:

If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for an object, the object and its subobjects, if any, have an indeterminate initial value; if the object or any of its subobjects are of const-qualified type, the program is ill-formed.

See Why does C++ require a user-provided default constructor to default-construct a const object?. Supposedly the program is ill-formed if the object is of const-qualified POD type, and there is no initializer specified (because POD are not default initialized). Note how g++ behaves for the following:

struct A {int a;};
struct B {int a = 1;};
int main() 
{
    A a;
    B b;
    const A c; // A is POD, error
    const B d; // B is not POD, contains data member initializer, no error
}

Solution 4:

Since C++17, this code is correct, as is the similar code from this question:

struct MyClass1 { int i{}; };
struct MyClass2 { const MyClass1 m; };
MyClass2 a;

clang 8.0.0 rejects this latter code even with -std=c++17 which means that clang 8.0.0 has a bug.

In C++17 the following new text was added as [dcl.init]/7 (as per P0490R0 in response to DR 253):

A class type T is const-default-constructible if default-initialization of T would invoke a user-provided constructor of T (not inherited from a base class) or if

  • each direct non-variant non-static data member M of T has a default member initializer or, if M is of class type X (or array thereof), X is const-default-constructible,
  • if T is a union with at least one non-static data member, exactly one variant member has a default member initializer,
  • if T is not a union, for each anonymous union member with at least one non-static data member, exactly one non-static data member has a default member initializer, and
  • each potentially constructed base class of T is const-default-constructible.

If a program calls for the default-initialization of an object of a const-qualified type T , T shall be a const-default-constructible class type or array thereof.


Prior to C++17 there was no such text; an object defined as const must either have an initializer or a user-provided constructor. So, prior to C++17, clang was correct and g++ was bugged to accept the code without diagnostic.