2

I'm trying to use a static member in a normal member function. But the compiler report some errors. Pls take a look at this code

#include <memory>

template<typename T>
class MyClass {
private:
  static std::allocator<T> alloc;
  T *p;
public:
  void assign(T e) {
    p = alloc.allocate(1);
    alloc.construct(p, e);
  }
};

and this is how I use it:

#include 'myclass.h'

int main() {
  MyClass<int> cls;
  cls.assign(4);

};

And the compiler give this error:

/Users/liuziqi/CLionProjects/cpplearning/src/tt.h:17:9: warning: instantiation of variable 'MyClass<int>::alloc' required here, but no definition is available [-Wundefined-var-template]
    p = alloc.allocate(1);
        ^
/Users/liuziqi/CLionProjects/cpplearning/src/main.cpp:49:7: note: in instantiation of member function 'MyClass<int>::assign' requested here
  cls.assign(4);
      ^
/Users/liuziqi/CLionProjects/cpplearning/src/tt.h:13:28: note: forward declaration of template entity is here
  static std::allocator<T> alloc;
                           ^
/Users/liuziqi/CLionProjects/cpplearning/src/tt.h:17:9: note: add an explicit instantiation declaration to suppress this warning if 'MyClass<int>::alloc' is explicitly instantiated in another translation unit
    p = alloc.allocate(1);

I can not figure out which part is wrong.....I have defined that static member and any member function should be able to use it. Is this error relevant to template? (I've just learned template and not sure If I use it correctly.)

Ziqi Liu
  • 2,931
  • 5
  • 31
  • 64

1 Answers1

4

I'm going to take a shot at describing what the warning is referring to in addition to answering the "how to fix it" question (which certainly has been answered many times before)...

Because MyClass is a template, the compiler expects all the code for the templated class to be available in the same file (myclass.h). Since you only declare but do not define alloc in myclass.h, the compiler assumes you made a mistake. It's not absolutely required to be there (hence the warning and not error) and you could disable the warning if you define the variable elsewhere, but it's almost certainly just a mistake.

If you're using c++17, the easiest way to deal with this is to declare the static member as inline, so it will be defined right there:

  static inline std::allocator<T> alloc;

live: https://godbolt.org/g/cr1aUP

Prior to C++17, you would explicitly define the variable in myclass.h:

template<typename T>
std::allocator<T> MyClass<T>::alloc;

live: https://godbolt.org/g/2Znqst

xaxxon
  • 19,189
  • 5
  • 50
  • 80
  • https://stackoverflow.com/questions/3229883/static-member-initialization-in-a-class-template – xaxxon Jul 30 '18 at 00:48
  • I'm a little bit confused about 'declare' and 'define' here. I thought my declaration of alloc inside the class template is already an 'definition', because that's how I normally define a member of class. Why still need to define it outside? (And it looks the same as my 'declaration' inside the template.....what's the different here?) . Is it the problem of the template? (in other word, if we're not writing a template, but a class here, then the definition inside the class should be enough) – Ziqi Liu Jul 30 '18 at 01:03
  • static variables are only defined once per class, not once per object. Prior to C++17, that required a static member definition outside the class definition. It's very similar to how `extern` works in a non-class setting, if you're familiar with that. If this weren't in a template situation, it's quite normal to have the definition in another file (a .cpp file), but since the static variable needs to be defined for each *instantiation* of the class MyClass needs one, MyClass needs a different one, etc... then the definition is expected in the .h file so the instantiating... – xaxxon Jul 30 '18 at 01:11
  • caller can generate the code to create the static member for the exact instantiated type of the templated class being used. – xaxxon Jul 30 '18 at 01:11