class CountedClass {
static int count;
public:
CountedClass() { ++count; }
CountedClass(const CountedClass&) { ++count; }
~CountedClass() { --count; }
static int getCount() { return count; }
};
int CountedClass::count = 0;
int main() {
CountedClass a;
cout << CountedClass::getCount() << endl; // 1
CountedClass b;
cout << CountedClass::getCount() << endl; // 2
{ // an arbitrary scope:
CountedClass c(b);
cout << CountedClass::getCount() << endl; // 3
a = c;
cout << CountedClass::getCount() << endl; // 3
}
cout << CountedClass::getCount() << endl; // 2
} ///:~.
All constructors of CountedClass increment the static data member count, and the destructor decrements it. The static member function getCount( ) yields the number of current objects whenever it called.
It would be tremendously tedious to have to manually add these members every time you wanted to add object counting to a class. What is the usual object-oriented device to which one turns to repeat or share code? It’s inheritance, of course, which, unfortunately, is only half a solution in this case. Observe what happens when we collect the counting logic into a base class.
//: C05:CountedClass2.cpp
// Erroneous attempt to count objects
#include
using namespace std;
class Counted {
static int count;
public:
Counted() { ++count; }
Counted(const Counted&) { ++count; }
~Counted() { --count; }
static int getCount() { return count; }
};
int Counted::count = 0;
class CountedClass : public Counted {};
class CountedClass2 : public Counted {};
int main() {
CountedClass a;
cout << CountedClass::getCount() << endl; // 1
CountedClass b;
cout << CountedClass::getCount() << endl; // 2
CountedClass2 c;
cout << CountedClass2::getCount() << endl; // 3 (Error)
} ///:~
All classes that derive from Counted share the same, single static data member, so the number of objects is tracked collectively across all classes in the Counted hierarchy. What is needed is a way to automatically generate a
//: C05:CountedClass3.cpp
#include
using namespace std;
template
class Counted {
static int count;
public:
Counted() { ++count; }
Counted(const Counted
~Counted() { --count; }
static int getCount() { return count; }
};
template
int Counted
// Curious class definitions
class CountedClass : public Counted
class CountedClass2 : public Counted
int main() {
CountedClass a;
cout << CountedClass::getCount() << endl; // 1
CountedClass b;
cout << CountedClass::getCount() << endl; // 2
CountedClass2 c;
cout << CountedClass2::getCount() << endl; // 1 (!)
} ///:~.
Each derived class derives from a unique base class that is determined by using itself (the derived class) as a template parameter! This may seem like a circular definition, and it would be, had any base class members used the template argument in a computation. Since all data members of Counted are not dependent on T, its size (which is zero!) is known when the template is parsed. It doesn’t matter, therefore, which argument is used to instantiate Counted; its size is always the same. Therefore, any derivation from an instance of Counted can be completed when it is parsed, and there is no recursion. Since each base class is unique, it has its own static data, thus constituting a handy technique for adding counting to any class whatsoever. Jim Coplien was the first to mention this interesting derivation idiom in print, which he cited in an article, entitled "Curiously Recurring Template Patterns."[ ][65]
Template metaprogramming
In 1993 compilers were beginning to support simple template constructs so that users could define generic containers and functions. About the same time that the STL was being considered for adoption into standard C++, clever and surprising examples such as the following were passed around among members of the standards committee:.
//: C05:Factorial.cpp
// Compile-time computation!
#include
using namespace std;
template
struct Factorial {
enum {val = Factorial
};
template<>
struct Factorial<0> {
enum {val = 1};
};
int main() {
cout << Factorial<12>::val << endl; // 479001600
} ///:~
That this program prints the correct value of 12! is not alarming. What is alarming is that the computation is complete before the program even runs!.