7

clang++ does not ever allow default initialization of a const variable of class-type without a user-defined constructor; g++ is slightly less restrictive (see below). According to this answer, this is because POD types "are not initialized by default." If I understand correctly, this means that default-initialization does not invoke the default-constructor, nor does it invoke value-initialization, so the data members inside the POD type are not initialized. Of course it makes no sense to have a const POD type with uninitialized values in it, since they can never be initialized and therefore are not safe to use.

There are a few variants of this situation:

  1. The type is technically "POD", but contains no data members (only functions). (clang++ does not treat this as a special case, nor, I think, does the standard, but g++ does allow it, even when the constructor is marked explicit.)
  2. An empty constructor is defined using {}. (This is the recommended workaround on the clang page describing the issue.)
  3. The default constructor is declared =default. (C++11 onward; the type is still considered POD, so neither compiler nor the standard treat it as a special case.)
  4. Aggregate initialization is explicitly invoked using {}, which (if I understand correctly) becomes value-initialization. (C++11 onward; both compilers—and, I think, the standard—allow this.)

In the first case, there can be no uninitialized members, so it's unclear why any instantiation of the class itself would ever be considered "uninitialized," regardless of whether or not it's const. Since g++ allows this behavior, is it safe to use? Why is it prohibited by clang++ and the standard? (And are there any other cases where g++ allows POD-default-initialization where clang++ does not?)

In the second and third cases, the requirement of using {} instead of =default seems odd to me. EDIT: this question explains the difference quite well, so I've removed the part of the question asking about the distinction. (I still think it's a terribly confusing aspect of the language, though.)

Finally, will Foo f{} always zero-initialize members of built-in type if Foo::Foo(void) is {}, =default, or implicitly-declared?

Community
  • 1
  • 1
Kyle Strand
  • 15,941
  • 8
  • 72
  • 167
  • Is there a use-case for a const POD type with no member variables? You may as well use a non-const one, since there are no members that could be erroneously changed. – M.M Jun 05 '15 at 04:02
  • @MattMcNabb I'm not sure. This is a pattern that recurs repeatedly in a colleague's code, in templated factory functions (or...something like that). I'll try to remember to ask said colleague tomorrow what the intent is. – Kyle Strand Jun 05 '15 at 04:06
  • A constructor defined with `{}` is not a trivial constructor. – David G Jun 05 '15 at 04:12
  • @0x499602D2 Good lord this language has terrible terminology. What's the appropriate name? Is there one? – Kyle Strand Jun 05 '15 at 04:14
  • 1
    @KyleStrand I'd say "empty" – M.M Jun 05 '15 at 04:16
  • @MattMcNabb the idea is apparently that the signature of the function that returns a const reference (partly) indicates a level of thread safety. – Kyle Strand Jun 06 '15 at 20:24

1 Answers1

5

Since g++ allows this behavior, is it safe to use?

Safe in what sense? It's obviously not portable currently. However, it's not going to give you unexpected results on g++.

Why is it prohibited by clang++ and the standard?

Presumably the committee either didn't think about it when they put the "user-provided constructor required" rule in C++98, or decided that special-casing empty POD classes isn't worth the specification complexity; clang++ is just following the standard. However, the standard is likely going to change to more generally allow you to write const Foo f; as long as the constructor will, in fact, initialize every subobject.

Finally, will Foo f{}; always zero-initialize members of built-in type if Foo::Foo(void) is {}, =default, or implicitly-declared?

Yes for the latter two. No for the first. That one counts as user-provided, so value-initialization doesn't perform zero-initialization before calling the default constructor.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • I did indeed mean "safe" in the "unexpected results" sense. Once again, nice open-standard link; how do you find those so quickly (or at all)? Also, when you say "value-initialization doesn't perform zero-initialization before calling the default constructor," do you mean that zero-initialization won't (necessarily) happen *at all*? So changing from `=default` to `{ }` can actually *introduce* improper-initialization bugs? – Kyle Strand Jun 05 '15 at 04:18
  • @KyleStrand 1) This particular g++/clang++ difference has been asked about on SO before, so I know that there's an open core issue on this :) 2) Yes, it's not guaranteed to happen, though the implementation can clobber the memory however it likes (as no well-defined program can observe it). Technically speaking, it's indeterminate, and reading it causes UB (modulo a few exceptions for `unsigned char`); 3) Yes. – T.C. Jun 05 '15 at 04:24