17
votes

Consider this code snippet:

struct Foo {

};

template<typename T>
struct Bar {
    const T foo;
};

int main() {
    Bar<Foo> test;
}

I'm compiling it with g++-4.9.2 with [-std=c++11 -O0 -g3 -pedantic -Wall -Wextra -Wconversion] and getting error: uninitialized const member in ‘struct Bar<Foo>’. This is pretty obvious.

BUT try just add std::string as Foo member and program compiles!

#include <string>
struct Foo {
    std::string test;
};
// (...)

What happening? Replacing test's type into double causes program to fail to compile again. What string member changes in class?

Link to online compiler with this snippet.

It seems that gcc behaves like that since version 4.6.

3
Seems to be a GCC bug clang does not share: coliru.stacked-crooked.com/a/508312aef603cc15 - Deduplicator
I initiality though that it is some missing warning but adding [-O0 -g3 -pedantic -Wall -Wextra -Wconversion] doesn't give any more compiler messages. - omikron
MSVC does not exhibit the problem either and refuses to compile for lack of default constructor. - Serge Ballesta

3 Answers

18
votes

I think it should consistently always produce an error. Clang does so. The C++ standard says in clause (4.3) of §12.1.4 that the default constructor is implicitly deleted when

— any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or equal-initializer does not have a user-provided default constructor,

As Foo does not have a user-provided default constructor, Bar should have an implicitly deleted default constructor and thus instantiating Bar<Foo> test in main should produce an error.

Maybe report a bug to GCC?

4
votes

If you have const data member in your class/struct, then compiler won't generate default constructor for that. You have to explicitly define it and initialize that const member ( not assign it ).

It should be an error in both cases.

2
votes

It looks like g++ automatically generates a default constructor even when const members should have been initialized at construction time because it knows that a string has a default constructor initializing it to an empty string. In fact all happens as if the source was :

struct Foo {
    std::string test;
    Foo():test() {;}
};

template<typename T>
struct Bar {
    const T foo;
    Bar(): foo() {;}
};

int main() {
    Bar<Foo> test;
}

which compiles fine both with clang and MSVC.

(but I must admit I still haven't found the reference in gcc doc explaining that ...)