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 ofT
would invoke a user-provided constructor ofT
(not inherited from a base class) or if
- each direct non-variant non-static data member
M
ofT
has a default member initializer or, ifM
is of class typeX
(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.