Why Does C++ Require a User-Provided Default Constructor to Default-Construct a Const Object

Why does C++ require a user-provided default constructor to default-construct a const object?

This was considered a defect (against all versions of the standard) and it was resolved by Core Working Group (CWG) Defect 253. The new wording for the standard states in http://eel.is/c++draft/dcl.init#7

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 (if any), 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.

This wording essentially means that the obvious code works. If you initialize all of your bases and members, you can say A const a; regardless of how or if you spell any constructors.

struct A {
};
A const a;

gcc has accepted this since 4.6.4. clang has accepted this since 3.9.0. Visual Studio also accepts this (at least in 2017, not sure if sooner).

Declaring a const object in C ++ requires a user-defined default constructor. If I have a mutable member variable, why not?

Rewriting my comment as an answer, hope it could helps someone.

It makes no sense declaring a const object if it is not initialized in some form.

Consider the following code:

    const int x;

clang says: error: default initialization of an object of const type 'const int'.

gcc would say: error: uninitialized const ‘x’ [-fpermissive]

The logic behind this is that there is no sense in this type of declaration.

The value of x can never change, and therefore this code would be unpredictable as x would be mapped to uninitialized memory.

In your example, adding the keyword mutable to value means that although the Some instance is constant when declared as:

    const Some some;

It is still possible to change value at a later time.

For example:

    some.value = 8;

This means it is possible to use this code in a predictable manner, since value can be set later, and there are no uninitialized constants.

Why do we need a user-provided constructor for a const object?

In the first case, the compiler can tell, from just the class definition (not the definition of any members) that the variable won't be initialised. The class definition must be available in order to define the variable, so it's simple to state a rule to say that says the program is ill-formed if a const variable is left uninitialised in that case.

In the second, it would need the definition of the user-declared constructor to determine whether or not it's initialised. That's not necessarily available when the compiler is dealing with the variable definition, so in general it can't check whether or not the constructor initialises everything; the constructor definition isn't necessarily available. As is often the case, a diagnosis isn't required even in those cases like this, where the constructor's definition is available and the compiler could figure it out.

Why does gcc allow a const object without a user-declared default constructor but not clang?

The spec currently requires user-provided default constructors but it appears that GCC is implementing a change based on DR 253 which says that if all sub-objects would be initialized without a user provided default constructor then a user-provided default constructor is not required.

This change is only draft status, has not been accepted yet and is not part of the standard. So I think think this is behavior intended by GCC developers but I'm not sure if this is a conforming extension though.

Here's a change to the first example which causes GCC to produce an error:

class A {
public:
void f() {}

int i;
};

int main()
{
A a; // OK
const A b; // ERROR

a.f();
return 0;
}

Note that gcc downgrades the error to a warning with the -fpermissive flag.

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=42844

Why const object must have a user-provided constructor?

Because the language rules say so.

The constant must have its value set in the definition, as it cannot be assigned a value later. If you don't explicitly provide a value, the type must have a default constructor.

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

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.

Why does a user-provided constructor allow for instantiation of a const class instance?

Why does a user-provided constructor allow for instantiation of a const class instance?

Because the default constructor is responsible for initialisation of the object.

Sure, in this case the constructor fails to initialise the member in this case, but the compiler cannot generally know that whether it does that. Because a user defined constructor is used, it may make sense and thus there is no reason to disallow that.

The original user defined constructor in the cpp reference example does absolutely nothing and still initializes mem

It "default initialises" which is what happens when you don't initialise something.

default initialization of const qualified type with no user provided constructor

According to the draft standard 8.5/p7.3 Initializers [dcl.init]:

(7.3) — Otherwise, no initialization is performed

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.

So you're right, a const-qualified object must have a user-provided constructor to be initialized.

This is due to fact that const-qualified objects are initialized once and if no default constructor is provided then the object would contain uninitialized values.

However, in your example class Test has no member variables. Strictly speaking, according to the standard is ill formed, but there's no harm since Test has no member variables.

For this reason the comittee filed a Defect Report DR 253. That says:

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

GCC follows that DR that's why it compiles the code, my guess is that for the same reason VC++ compiles the code as well.

However if you try to compile the following code:

class Test{ 
int i;
};

int main() {
const Test t;
}

GCC will issue an error. VC++ 2015 will emit a diagnostic:

warning C4269: 't': 'const' automatic data initialized with compiler
generated default constructor produces unreliable results

A defaulted default constructor, why is it not a user-provided default constructor?

Because the standard says so ([dcl.fct.def.default]/5):

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

Doing it this way allows you to maintain the triviality property with = default;. Otherwise, there's no way to give a class with another constructor a trivial default constructor.

no user-provided default constructor warning only with gcc

it just looks like a bug in older version of static analyzer, which couldn't detect fact that you assign values to the elements and were warning you about using an uninitialized class and message can't be avoided by compiler flags - disabling pedantic mode and all warning doesn't remove it, as res supposed to be a returned constant, it have to be initialized.

Simplest way to suppress it, would be to add value initialization, which would have no cost for compile-time initialization:

    atype res {};

Also it looks like more idiomatic std::generate will be applicable here with C++20. Note - with C++17 your fill_array() isn't a constexpr due to returning atype res but you don't see a error because you allowed x to be runtime.

Proper get_array for C++17

static constexpr atype get_array()
{
atype res {};
for (std::size_t i = 0; i < size; i++)
res[i] = 1;
return res;
}


Related Topics



Leave a reply



Submit