1
struct foo{
   int bar1 = 5;
   const static int bar2 = 6;
};

Sharing foo using a header file among multiple translation units doesn't cause a link error for bar1 and bar2, why is that?

To my knowledge, bar1 doesn't even exist until an instance of foo is created and each instance of bar1 will have unique symbol, so no link errors can happen. bar2 is not even a definition, it's a declaration with initializer and needs to be initialized with const int foo::bar2; in only one file so again no link errors can happen.

Referring to this answer: https://stackoverflow.com/a/11301299

Is my understanding correct?

Dan
  • 2,694
  • 1
  • 6
  • 19
  • No link errors if it is not ODR-used. What you probably want is `static inline int const bar3 = 7;` (note the extra `inline`) to have all the `bar3` uses to have weak linkage, or if given as `static constexpr int bar3 = 7;` (which has an implicit `inline`). Not sure if you were aware of that option, hence this comment. – Eljay Jan 16 '22 at 19:45
  • I thought inline is only applicable to functions? either way, is my interpretation of this correct? – Dan Jan 16 '22 at 20:03
  • inline variables were introduced in c++17 – n. m. could be an AI Jan 16 '22 at 20:05

2 Answers2

2

As long as foo::bar2 is not odr-used, there is nothing wrong with the program. You can even use foo::bar2 in ways that are not odr-uses, and it will be fine. For example:

std::cout << foo::bar2 + 1;  // prints 7

This is because foo::bar2 in this expression can simply be replaced with its compile-time constant value of 6. It is not required to have an address, therefore its definition is not required to exist.

As soon as you odr-use foo::bar2 without having provided a definition, the program becomes ill-formed, no diagnostic required. Usually, this will cause a link error. You can see it for example in code like this:

std::vector<int> v;
v.push_back(foo::bar2);

This is an odr-use of foo::bar2 because push_back takes its argument by reference, necessitating the existence of its address. The program will probably not build unless an out-of-line definition of foo::bar2 is provided somewhere.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • how about `bar1`? is my explanation of this correct? – Dan Jan 16 '22 at 20:10
  • @Dan It's a non-static data member. Its declaration already serves as the definition. – Brian Bi Jan 16 '22 at 20:13
  • your answer may be compiler specific and not a standard. Also `declaration already serves as the definition`? what do you mean by that and what does it have to do with my question? – Dan Jan 16 '22 at 21:55
  • @Dan You don't need to provide any separate definition of `foo::bar1` because you've already defined it in the struct definition simply by giving its name and type in the struct definition. This is true for all non-static data members. – Brian Bi Jan 16 '22 at 21:58
  • @Dan Depending on what toolchain you have, you may or may not get an error in the cases where you have odr-used something without a definition. Most likely you will get an error, but it's not guaranteed. Other than that, my answer is not compiler-specific. – Brian Bi Jan 16 '22 at 21:59
  • But `int bar1 = 5;` is the the definition, `int bar1;` would be the declaration? your are saying `int bar1;` as a member variable is also a definition? how? it doesn't have a value. – Dan Jan 16 '22 at 22:09
  • @Dan when you declare a non-static data member, it's a definition even if there's no initializer. If you don't provide an initializer in the declaration, one can still be provided by a constructor. – Brian Bi Jan 16 '22 at 22:23
  • So going back to my question, why doesn't `struct foo{ int bar1 = 5;};` cause link errors. Is it because `bar1` doesn't exist until an instance of the struct is created? – Dan Jan 16 '22 at 22:28
  • @Dan it doesn't cause link errors because there's nothing wrong with it that would lead to a link error. If you want me to be more specific then you should explain why you think a link error might happen. – Brian Bi Jan 16 '22 at 23:38
2

Violations of The One Definition Rule do not require a diagnostic or a compiler error.

Presuming that bar2 is referenced in some translation unit: some compilers may successfully compile and link the resulting code. Other compilers may not and reject it (presumably at the linking stage). The C++ standard indicates that this is ill-formed, but a formal compiler diagnostic is not mandatory. That's just what the C++ standard says.

The reason why some compilers may let this skate is because they'll inline all the references to the static class member, hence there won't be any unresolved references that produce a failure at the link stage.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148