Reputation: 6858
If I want to make a template class, and depending on the typeid of the template parameter perform different actions, then how do I code this?
For instance, I have the following template class, in which I want to initialize the member field data depending on whether it is an int or a string.
#include <string>
template <class T>
class A
{
private:
T data;
public:
A();
};
// Implementation of constructor
template <class T>
A<T>::A()
{
if (typeid(T) == typeid(int))
{
data = 1;
}
else if (typeid(T) == typeid(std::string))
{
data = "one";
}
else
{
throw runtime_error("Choose type int or string");
}
}
This code would not compile however, with the following main file.
#include "stdafx.h"
#include "A.h"
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
A<int> one;
return 0;
}
The error is: error C2440: '=' : cannot convert from 'const char [2]' to 'int', which means the code is actually checking the else-if statement for an int, even though it will never be able to reach that part of the code.
Next, following this example (Perform different methods based on template variable type), I tried the following A.h file, but I got several linker errors mentioning that A(void) is already defined in A.obj.
#include <string>
template <class T>
class A
{
private:
T data;
public:
A();
~A();
};
// Implementation of constructor
template <>
A<int>::A()
{
data = 1;
}
template <>
A<std::string>::A()
{
data = "one";
}
Does anybody know how to get this code up and running? I also realize that using such an if-else statement in a template class might remove the power from a template. Is there a better way to code this?
EDIT: after discussion with Torsten (below), I now have the following A.h file:
#pragma once
#include <string>
// Class definition
template <class T>
class A
{
public:
A();
~A();
private:
T data;
};
// Implementation of initialization
template < class T >
struct initial_data
{
static T data() { throw runtime_error("Choose type int or string"); }
};
template <>
struct initial_data< int >
{
static int data() { return 1; }
};
template <>
struct initial_data< std::string >
{
static std::string data() { return "one"; }
};
// Definition of constructor
template <class T>
A<T>::A()
: data( initial_data< T >::data() )
{
}
and the following main:
#include "stdafx.h"
#include "A.h"
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
A<int> ione;
return 0;
}
The linker error I now get is: Test template 4.obj : error LNK2019: unresolved external symbol "public: __thiscall A::~A(void)" (??1?$A@H@@QAE@XZ) referenced in function _wmain
Upvotes: 2
Views: 3783
Reputation: 52284
Explicit specializations are the way to go.
I assume that you are including your A.h in several .cpp, and that's the root cause of your problem.
Specializations are definitions and there must be only one definition of A::A() and A::A() and so they must be in only one .cpp.
You'll have to move the explicit specialization in a .cpp
template <>
A<int>::A()
{
data = 1;
}
template <>
A<std::string>::A()
{
data = "one";
}
and keep a declaration for them in A.h
template<> A<int>::A();
template<> A<std::string>::A();
so that the compiler knows they are explicitly specialized and doesn't try to add automatic one.
Edit: with these four files, g++ m.cpp f.cpp a.cpp doesn't show any errors.
// a.h
#define A_H
#include <string>
template <class T>
class A
{
private:
T data;
public:
A();
};
template<> A<int>::A();
template<> A<std::string>::A();
#endif
// a.cpp
#include "a.h"
template <>
A<int>::A()
{
data = 1;
}
template <>
A<std::string>::A()
{
data = "one";
}
// f.cpp
#include "a.h"
int f()
{
A<int> one;
A<std::string> two;
}
// m.cpp
#include "a.h"
int f();
int main()
{
A<int> one;
A<std::string> two;
f();
}
Upvotes: 3
Reputation: 2347
You are correct in the second solution, what you need is template specialisation (keeping declaration and implementation together):
#include <string>
template <class T>
class A
{
private:
T data;
public:
A();
~A();
};
template <>
class A <std::string>
{
private:
std::string data;
public:
A() { data = "one"; }
};
template <>
class A <int>
{
private:
int data;
public:
A() { data = 1; }
};
If I may suggest a more elegant solution, then I would add a parameter to the constructor and avoid the template specialisation:
template <class T>
class A
{
private:
T data;
public:
A( T value ) : data( value ) {}
virtual ~A() {}
};
Upvotes: 3
Reputation: 2555
In case it's just the c'tor where you want to have behavior that depends on T, I would suggest to factor this out to a different struct:
template < class T >
struct initial_data
{
static T data() { throw runtime_error("Choose type int or string"); }
};
template <>
struct initial_data< int >
{
static int data() { return 1; }
}
template <>
struct initial_data< std::string >
{
static std::string data() { return "1"; }
}
If you specialize a class on it's template parameter, the different specializations are totally different types and can have different sets of data and functions.
Finally:
template <class T>
A<T>::A()
: data( initial_data< T >::data() )
{
}
kind regards Torsten
Upvotes: 2