43

I have the code:

class A {
  public:
    A() = default;

  private:
    int i = 1;
};

int main() {
  const A a;
  return 0;
}

It compiles fine on g++ (see ideone), but fails on clang++ with error:

default initialization of an object of const type 'const A' requires a user-provided default constructor

I reported this issue on LLVM bug-tracker and got it INVALID.

I see it absolutly pointless to try to convince the clang developers. On the other side, I don't see the reason for such restriction.


Can anyone advise, if the C++11 Standard somehow implies this code to be invalid? Or should I just report a bug to g++? Or maybe there is enough freedom in language rules to handle this code in many ways?

abyss.7
  • 13,882
  • 11
  • 56
  • 100
  • 4
    It could be that the wording of the standard requires a user-provided constructor. But that rule would make no sense in this case. Anyway, the `A() = default;` can be removed, it is just a distraction. – juanchopanza Feb 20 '14 at 06:46
  • I suggest to remove the `c++` tag. Google currently sees this page title: *c++ - Do I really need to implement user-provided constructor for const objects? - Stack Overflow* which is somewhat misleading. – Wolf Feb 20 '14 at 10:09
  • 1
    It's a defect in the C++ standard (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#253), but C++11 appears not to have implemented the correction suggested at the August 2011 meeting (too late by then, perhaps?). I wonder if the issue was simply overlooked given that it was reported in 2000. – Steve Jessop Feb 20 '14 at 20:00
  • @SteveJessop Thought so, thanks for finding the reference. – juanchopanza Feb 20 '14 at 20:02
  • @juanchopanza: and if it's not fixed in N3797 either, that suggests it won't be even though the committee was reportedly persuaded of the case on that occasion in 2011. Looks like the kind of thing that if it happened at work in a document we produced, I'd be figuring out who has to send a strongly-worded email to whom in order to make it really happen ;-) – Steve Jessop Feb 20 '14 at 20:09
  • @SteveJessop See my answer. The note from 2011 is not marked as a "suggested resolution"; it looks more like something agreed principle after debate. As you can see in comments below Jonathan Wakely believes it's implementable and has done so, but others might think that spec isn't ready for implementation. – Potatoswatter Feb 20 '14 at 22:10
  • @Potatoswatter: ah thanks, I'm not familiar enough with defect reports to realise that "should do X" is missing the magic words to make it happen. I guess it's a question of Jonathan making it his personal mission to see it through, and then if nobody objects eventually it'll get properly agreed instead of just vaguely agreed :-) – Steve Jessop Feb 20 '14 at 22:15
  • Possible duplicate of [Why does C++ require a user-provided default constructor to default-construct a const object?](https://stackoverflow.com/questions/7411515/why-does-c-require-a-user-provided-default-constructor-to-default-construct-a) – David Stone Nov 18 '17 at 16:34

4 Answers4

24

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.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • Ok yeah, N3485 has this as well for C++11 (and right underneath where I was reading, too :/) C++03 doesn't seem to have this rule in there at all. Instead, there's: *If T is a cv-qualified type, the cv-unqualified version of T is used for these definitions of zero-initialization, default-initialization, and value-initialization.* – chris Feb 20 '14 at 06:55
  • I suspect this goes back much further and it's essentially legacy cruft. But I haven't copied the C++98/C++03 files onto this computer, and the other one isn't running ATM :P – Potatoswatter Feb 20 '14 at 06:57
  • 4
    FWIW GCC seems to attempt to do the sensible thing, failing compilation if the data member is not initialized at the point of declaration, and emitting errors if it is. I would be tempted to call this a bug in the standard. – juanchopanza Feb 20 '14 at 07:12
  • @juanchopanza: that's the issue with gcc though :( It's one thing to be enthusiast about fixing defects in the Standard, but it's another to accept invalid code and thus non-portable code. – Matthieu M. Feb 20 '14 at 07:35
  • 1
    @MatthieuM. I'm not saying GCC is right to do it. I would prefer standards compliance any time. But the GCC behaviour seems more sensible (caveat: I have not carefully read the related parts of the standard yet.) – juanchopanza Feb 20 '14 at 08:03
  • 5
    The GCC "bug" is not going to be fixed, the standard is, and then Clang will be. The GCC behaviour is [documented](http://gcc.gnu.org/gcc-4.6/changes.html#cplusplus) and by design because it broke too much code. – Jonathan Wakely Feb 20 '14 at 09:23
  • @JonathanWakely Yeah, I suspected as much. As noted above, it couldn't be more than a pedantic warning in any case. – Potatoswatter Feb 20 '14 at 09:28
23

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.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Nice work-around, but would you have an opinion on whether this is a bug in the standard? – juanchopanza Feb 20 '14 at 07:14
  • @juanchopanza: I have added quotes to the relevant parts of the standard – Dietmar Kühl Feb 20 '14 at 07:31
  • Ignoring the defaulted constructor, I think the fact of having the data member initialized at the point of declaration voids the reason for the requirement of having a user provided constructor. – juanchopanza Feb 20 '14 at 08:04
  • @juanchopanza: the clause in `const` initialization doesn't make the presence of the initializer a special case. Maybe it should but I can also see reasons why it may not do so. I _guess_ it is an oversight in the standard but as written the standard is clear: the original code is incorrect. – Dietmar Kühl Feb 20 '14 at 10:24
  • Yes, I agree that as per the standard, clang is right to produce an error. Steve Jessop just came up with this defect report, which makes sense: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#253 – juanchopanza Feb 20 '14 at 20:03
4

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
}
  • 1
    TL;DR: in 2010, before C++11 was ratified, GCC was updated to comply with the C++03 rule. And the C++03 rule happens to make perfect sense for C++11, despite the fact it was changed. – Potatoswatter Feb 20 '14 at 07:59
2

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.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • It sounds like an empty struct is still not const-default-constructible, and a test with gcc-9 confirms that that's how they've interpreted it too. – John Ilacqua Aug 02 '19 at 00:32
  • 1
    @JohnIlacqua I'd interpret it as saying the empty struct IS const-default-constructible, under the first bullet point: it's still true that each member has an initializer when there are no members – M.M Aug 02 '19 at 01:03
  • true, that would be consistent with `all_of` returning true for an empty range. – John Ilacqua Aug 02 '19 at 01:08