5

What should happen if an expression's type is not dependent, but we use it to initialize a static auto variable? GCC and Clang differ in their behavior

template<typename T>
struct A {
   static inline auto x = sizeof(T{}.f);
};

A<int> a;

GCC doesn't raise an error. But Clang thinks that this is invalid because it instantiates the operand of "sizeof". GCC appears to skip that step because sizeof(T{}.f) always has type size_t (not type dependent), so it already knows type of x without instantiation. Both compilers conformly reject the program if we refer to x, for example by (void) a.x;.

Does it even have to resolve the type of x at all? With C++14 upwards the language allows keeping things (like functions) with a "placeholder type" and doing delayed instantiation to find out about the actual return type later on, if I remember correctly. Does it have to apply this to x aswell, so keeping x with a placeholder type till we refer to a.x?

What compiler is correct according to the Standards?


EDIT

Someone asked

uhm, shouldnt' this be equivalent to this ?

template<typename T>
struct A {
   static const std::size_t x;
};

template<typename T>
inline constexpr std::size_t A<T>::x = sizeof(T{}.f);

The difference, and what concerns me in my question, is that the static data member in my question is auto. Therefore, in order to know the type of x, you need to know the type of the initializer. Clang appears to instantiate the initializer eagerly in order to get the type. But GCC doesn't, apparently? I would like to understand what's going on.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212

1 Answers1

2

From [temp.inst]/3:

Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist; in particular, the initialization (and any associated side effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist.

Simply writing A<int> a; does noes not use A<int>::x in a way that requires its definition to exist, so its initialization should not occur. gcc is correct.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Not sure I'm completely confident. Playing devils advocate: Why does it not require the definition to exist? I compared it with `inline auto x;` at namespace scope and it complains "1 : :1:13: error: declaration of variable 'x' with deduced type 'auto' requires an initializer inline auto x;". So it appears it requires the initializer, which for the inline definition is part of the definition. – Johannes Schaub - litb Nov 10 '17 at 20:37
  • 1
    @JohannesSchaub-litb Wording seems pretty clear. Member isn't used at all. – Barry Nov 10 '17 at 20:46
  • not sure about what "use" means here. Certainly it's "used" by its own declaration in the determination of `auto`, one can claim. In particular, it doesn't use "odr-used" here. – Johannes Schaub - litb Nov 10 '17 at 21:00
  • @JohannesSchaub-litb If that were true, the highlighted clause would have no meaning whatsoever. – Barry Nov 10 '17 at 21:09
  • @JohannesSchaub-litb maybe you're right; indeed [this](https://godbolt.org/g/1Kxe2j) fails to compile for both gcc and clang, both instantiate the static member – Massimiliano Janes Nov 10 '17 at 21:26
  • @Barry that quote (the part in bold) talks about side-effects ( such as initialization ) not the instantiation of the member as such. The relevant part should be "the specialization of the member is implicitly instantiated when the specialization is referenced **in a context that requires the member definition to exist**", so the question is, does an inline auto declaration 'require' its definition ? I think it does ( and both compilers agree, as per the example linked in my last comment ) ... – Massimiliano Janes Nov 10 '17 at 21:30
  • @MassimilianoJanes ah very good point about the side-effects. So it really comes down to my initial question: "What should happen if an expression's type is not dependent, but we use it to initialize a static auto variable?". In the linked snippet, the type is dependent. In my example, it's always `size_t`. – Johannes Schaub - litb Nov 10 '17 at 21:56
  • @MassimilianoJanes that said, with `inline` static data members, this rule is badly stated because the data-inline declaration always is a definition, independent of the presence of the initializer. So it's questionable what "the definition of the member is implicitly instantiated" means here. – Johannes Schaub - litb Nov 10 '17 at 22:01
  • @MassimilianoJanes especially p2 with "causes the implicit instantiation of the declarations, but not of the definitions, .. of .. static data members, .." seems badly stated with regard to data-inline definitions. Taken literally, Clang and GCC cannot even instantiate the declaration of the static data member because it's also a definition. – Johannes Schaub - litb Nov 10 '17 at 22:04
  • "requires the member definition to exist" is weasel words IMO ; it would be difficult and time-consuming to provide a real specification here , so they leave it up to places like SO to argue about what requires existence and what doesn't – M.M Nov 10 '17 at 22:55
  • @M.M. yep, and compilers fail to agree even in something [as simple as this](https://godbolt.org/g/AUn2iX), and note that this time it's gcc that does *not* follow Barry's interpretation – Massimiliano Janes Nov 11 '17 at 07:52
  • @mass as far as I am aware, in your example it is not a definition but just a declaration if a static. IMO that is clearly a bug in GCC. – Johannes Schaub - litb Nov 11 '17 at 13:13
  • @johannes I know, the point of that example was just to show that these compilers behave unreliably when deciding what "instantiating a declaration, but not a definition" is supposed to concretely mean, to the point of making any argument purely academic ... – Massimiliano Janes Nov 11 '17 at 13:34
  • @MassimilianoJanes actually the wording was already weasily with normal member functions because for example `void f() { }` in a class template is also a definition. So instantiating "the declarations but not the definitions" is something weird to say. They could guess that it means to not instantiate the bodies. But in the static inline member case, it's not as clear what it should mean IMO. – Johannes Schaub - litb Nov 11 '17 at 14:16