I have a templated class Child which inherits from a non-templated Parent class. When I include Child‘s header in multiple .cpp files I get a LNK2005 error. This happens because Parent gets defined in multiple compilation units. When those units get linked together they cause the LNK2005 error.
In case you’re wondering, the purpose of Parent is to give Child one static variable for all Child instance, not just one for each Child< ”type” >.
My question is, how do I create a templated class which has a unique (across all Child instances) static variable and can be included in multiple .cpp files?
Here’s a toy example that causes this LNK2005 error:
main.cpp
#include "Apple.h"
#include "Banana.h"
#include <string>
void main() {
Apple apple;
Banana banana;
}
Apple.h
#ifndef APPLE_H
#define APPLE_H
struct Apple {
Apple();
};
#endif // APPLE_H
Apple.cpp
#include "Apple.h"
#include "Child.h"
Apple::Apple() {
Child<int> child;
child.foo(5);
}
Banana.h
#ifndef BANANA_H
#define BANANA_H
struct Banana {
Banana();
};
#endif // BANANA_H
Banana.cpp
#include "Banana.h"
#include "Child.h"
Banana::Banana() {
Child<double> child;
child.foo(3.14);
}
Child.h
#ifndef CHILD_H
#define CHILD_H
#include <iostream>
using namespace std;
///////////// Parent Class Def ///////////
class Parent {
protected:
static int id;
};
int Parent::id = 0;
///////////// Child Class Def ///////////
template <class T>
struct Child : protected Parent {
Child();
void foo(T t);
};
template <class T>
Child<T>::Child() {
id++;
}
template <class T>
void Child<T>::foo(T t) {
cout << "Child" << id << "'s foo() says: " << t << endl;
}
#endif // CHILD_H
error LNK2005: “protected: static int Parent::id” (?id@Parent@@1HA) already defined in Apple.obj
You need to define
int Parent::id = 0;just once inChild.C. By including it in the header it gets defined once per file that includes your header.I suppose I should note that between the evils of putting it in
Parent.C(matching class name) orChild.Cmatching the header name, I picked consistency. Much better is to putParentin its own files so that you can put the definition in an aptly namedParent.Cwhich still matches the corresponding header name.