1

Hello if I have a static const data member then I can provide an in-class initializer for it and I don't need to define it again outside of the class body.

  • But that is true only if that constant is used within the class scope and if used outside, a separate definition outside must be supplied otherwise any reference to it causes a link-time error: "undefined reference to static object: x".

      struct Foo{
          static int const sz_ = 100;
          std::array<int, sz_> ai_100{};
      };
    
      //int const Foo::sz_;
    
      int main(){
    
          float pts[Foo::sz_]{}; // ok
          void bar(int const&); // defined later on
          bar(Foo::sz_); // undefined reference to Foo::sz_
      }
    
      void bar(int const&){
          //do_something
      }
    
  • Why when used the static const data member sz_outside of the class scope as the array size is OK?

  • Why when passing sz to function bar which takes an l-value reference to const the linker fails to link and complain about the definition of Foo::sz_?

  • The function bar takes an l-value reference to const int& so it can be initialized from an r-value so why it matters about the definition of the initializer Foo::sz_?

Itachi Uchiwa
  • 3,044
  • 12
  • 26

3 Answers3

3

Why we need a separate definition for a static const data member?

First, we must remember the One Definition Rule. It's actually a set of rules, but a simplified version of the rule that we are interested in this context is: There must be exactly one definition of every variable.

Secondly, we should consider that classes are often used in more than one translation unit.

If the declaration of the static member variable was a definition, then every translation unit that includes contains the class definition would contain that variable definition. That would be contrary to the ODR, and the linker wouldn't know what to do.

As such, there must be a separate definition of the variable which allows the class definition to be included into multiple translation unit while keeping the separate variable definition in one translation unit.


While the language implementations of past may not have been able to deal with multiple definitions (back before templates were in C++), these days they are, and the language has been expanded (in C++17) to allow inline definitions of variables. inline keyword has the same meaning for variable as it has for functions: It relaxes the One Definition Rule allowing (and also requiring) the variable to be defined in every TU (where it is odr-used).

eerorika
  • 232,697
  • 12
  • 197
  • 326
2

Mostly for historical reasons. Staring with C++17, you can have inline data members. And constexpr members are inline by default.

MSalters
  • 173,980
  • 10
  • 155
  • 350
2

Your non-linking example can be simplified to:

struct Foo{
    static int const sz_ = 100;
};

int main(){
    int const *p = &Foo::sz_;   // undefined reference to Foo::sz_
}

In other words, if we take the address of Foo::sz_, or assign a reference to it (which is a very similar operation, under the hood), then we need to define int const Foo::sz_; somewhere.

Why is this? Well, if we just use the value of Foo::sz_, then the compiler doesn't need a variable. It can just use 100 as, effectively, a literal. But if we want to take the address of it then we need an actual variable to refer to.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48