5

I'm trying to create some self-registering classes in C++. So I tried the solution similar to the one provided here. While doing this I stumble over something strange.

Here's the code:

#include <iostream>

class StaticClassType {
public:
  StaticClassType() {
    // Notify when the static member is created
    std::cout << "We're in." << std::endl;
  }
};


template<typename T>
class TestClass1 {
public:
  TestClass1() { &m; }
private:
  // Static member in a template class
  static StaticClassType m;
};

template<typename T>
StaticClassType TestClass1<T>::m;


class TestClass2 : public TestClass1<TestClass2> {
public:
  TestClass2() { } // required; why?
};


int main() {
  return 0;
}

This code create the static member variable TestClass1::m on startup (thereby printing "We're in." to the console) - i.e. before main() is started. However, the code only works if I write a (empty) constructor for TestClass2 (as shown in the example).

Why do I need to write this constructor? Why doesn't the default constructor generated by the compiler does the same thing?

This problem only occurs for template classes. If TestClass1 wasn't a template class, the code would work without writing the empty constructor for TestClass2.

Community
  • 1
  • 1
Sebastian Krysmanski
  • 8,114
  • 10
  • 49
  • 91

2 Answers2

2

I created even smaller example (without constructors, which are not needed) :

#include <iostream>

class StaticClassType {
public:
  StaticClassType(int v) {
    // Notify when the static member is created
    std::cout << "We're in."<<v << std::endl;
  }
};


template<typename T>
class TestClass1 {
protected:
  // Static member in a template class
  static StaticClassType m;
};

template<typename T>
StaticClassType TestClass1<T>::m = StaticClassType(3);


class TestClass2 : public TestClass1<TestClass2> {
public:
    void foo()
    {
        (void)m;
    }
};

int main() {
  std::cout << "main" << std::endl;
}

take a note that foo() method is needed, otherwise the compiler removes the static variable, since it's not used anywhere.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • depending on your compiler optimisation level, as foo isn't used anywhere, I'm not sure I'd guarantee m to be instantiated. – Tom Tanner Feb 09 '12 at 10:49
  • @TomTanner That is correct. The point is that the compiler is free to remove it if it is not used anywhere. – BЈовић Feb 09 '12 at 10:51
  • That's why I had the constructor in TestClass1. But still the question remains: Why does it work when I add an empty constructor to TestClass2? What's the difference to the one automatically generated by the compiler? – Sebastian Krysmanski Feb 09 '12 at 11:53
  • @SebastianKrysmanski You didn't say which compiler you used, but your workaround compiler with g++ produced warning: `statement has no effect`. I said why you need to access that static member variable : if you don't use a workaround, the compiler will remove it, and it will not be instantiated. – BЈовић Feb 09 '12 at 12:42
  • @VJovic: I used g++ as well as Visual C++ 2010. Both exhibited the same behavior. Yes you said why I need to access the static variable. But you didn't explain why adding the **empty** constructor to `TestClass2` solves the problem. (Or did I missing something?) But this was exactly my question. – Sebastian Krysmanski Feb 10 '12 at 08:27
  • @SebastianKrysmanski Both compilers decided that the static member variable is not used, and decided to remove it. I do not have a better explanation. Maybe with different settings, you get different behaviour. – BЈовић Feb 10 '12 at 09:54
0

From my point of view the "issue" is that in "Template world" the compiler will generate only what is really used by the client code. Here You never instanciate the TestClass2 class. Therefore a lot of code won't be generated. Try :

int main() {

  TestClass2 instance;
  return 0;
} 

And it works then.

yves Baumes
  • 8,836
  • 7
  • 45
  • 74