Home > front end >  Can const-default-constructible objects be of non-class types?
Can const-default-constructible objects be of non-class types?

Time:09-10

Per my understanding, for class type T to be const-default-constructible type, the default-initialization of T shall invoke a user-provided constructor, or T shall provide a default member initializer for each non-variant non-static data member: ([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

  • (7.4) each direct non-variant non-static data member M of T has a default member initializer [..]

Noting the bold part, it seems to me that const-default-constructible types can only be class types (including unions and structs). Therefore I can't say that const int, for example, is const-default-constructible type since int is not a class type.

You might ask, from where this confusion comes. Basically, [class.default.ctor]/2 says:

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

  • [..]
  • (2.4) any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or-equal-initializer is not const-default-constructible ([dcl.init]),
  • [..]

Notice the word "any". This bullet considers cases where X has data member M, and M is of class types as well as non-class types. But per [dcl.init]/7, the const-default-constructible types are limited to only be class types.

Consider the following example,

struct S
{
    const int I;
    // Is the type of S::I said to be non-const-default-constructible?

    S() = default;
};

Is there missing wording? Am I misreading the quotes?


PS: Thanks to @463035818_is_not_a_number for clarifying this. Consider this case:

struct X
{
    const int M = 0;
    // Is the type of X::M said to be const-default-constructible?

    X() = default;
};

CodePudding user response:

Is the type of S::I said to be non-const-default-constructible?

Yes. Like you've quoted in [dcl.init]/7 only class types can be const-default-constructible. The reason for this is non-class types do not have a default constructor, meaning they have no default value that can be used if they are declared like

const T foo;

When you read

any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or-equal-initializer is not const-default-constructible ([dcl.init])

It is saying that if you have a member in the form of const T name; in your class then the default constructor is deleted if T is not const-default-constructible. In your case that means your constructor is deleted because const int is not const-default-constructible.


In the case of X, M is still not const-default-constructible because it is not a class type. X though is const-default-constructible because M has a brace-or-equal-initializer so [class.default.ctor]/2.4 does not apply.


This can be boiled down into a simple rule: All const objects must have an initializer

Since built in types do not get default initialized they must have a value provided by the programmer.

CodePudding user response:

Non-class types are never const-default-constructible as you have read correctly.

But I don't think there is any wording missing. It seems intentional. The whole point of const-default-constructible is to make sure that you can't accidentally leave an object with an indeterminate value after initialization when you can't change that object's value later.

It would be UB to try to change the value of I after initialization (because it is declared const), but with the defaulted default constructor or without an explicit initializer on I, it would be default-initialized to an indeterminate value (because that is the effect of default-initialization on non-class types) which then is impossible to change. An indeterminate value can be used for almost nothing. The standard doesn't even allow copying it (except for types unsigned char and std::byte).

So there is no reason to allow you to default-construct this S with the defaulted default constructor.

(Technically the standard now allows using placement-new to replace the I object transparently as long as it is not part of a const-complete object, which allows changing its value in some sense, but that shouldn't be a normal use case.)

  • Related