Currently, I've implemented a template class Mat that's a wrapper around the matrix class of a third party library (T is the type of the components: double, int, etc.). Now, I wish to implement a Tensor class uses one Mat for storage and a second Mat for mapping indices.
I've templated the Tensor class as Tensor where T is the same as for the Mat class, Order and Dim are integers and are the order (or rank) and dimension (2 or 3) of the tensor. Sym is a boolean flag for symmetry.
The Tensor class will make use of Voigt notation to condense higher-order tensors onto matrices (for example, a 3-by-3-by-3-by-3 tensor can be mapped to a 6-by-6 matrix; this is done by mapping each pair of indices to a single index). In Voigt notation, a 3-by-3 tensor can be mapped to a 6-by-1 vector (matrix) but moving the (0,0) component in the tensor to the 0-location in the vector. similar (1,1) -> 1, (2,2) -> 2, (1,2)-> 3, (0,2)-> 4, and (0,1)-> 5. A similar rule exits for 2-by-2 tensors (they map to a 3-by-1 matrix).
To this end, I'd like my Tensor class to own the matrix:
0 5 4
5 1 3
4 3 2
If Dim == 3 and Sym == true. There are corresponding maps for unsymmetric tensors and 2D tensors (4 in all). These don't depend on the other template parameters (T and Order).
Thus, at what point do I specialize them? (Here, the question becomes applicable to anyone who has a templated class that needs a static const member to only partial specializations).
I've checked out this question here: Where to define static const member variables of a template class. But it doesn't discuss partial specialization.
So far, I have a forward declaration and a class definition in the same header file:
//cl_Tensor.hpp
namespace myNamespace
{
template< typename T, int Order, int Dim, bool Sym >
class Tensor;
}
template< typename T, int Order, int Dim, bool Sym >
class myNamespace::Tensor
{
protected:
myNamespace::Mat< T> mMat; // storage
static const myNamespace::Mat < uint > mTensorMap;
public:
// member functions and the like...
}
In my unit tests for my Tensor class, I can type:
template<> const moris::Mat< moris::uint> moris::Tensor< moris::real, 1, 2, true>::mTensorMap = { { 0, 2}, {2, 1} };
template<> const moris::Mat< moris::uint> moris::Tensor< moris::real, 1, 2, false>::mTensorMap = { { 0, 3}, {2, 1} };
template<> const moris::Mat< moris::uint> moris::Tensor< moris::real, 1, 3, true>::mTensorMap = { { 0, 5, 4}, {5, 1, 3}, {4, 3, 2} };
template<> const moris::Mat< moris::uint> moris::Tensor< moris::real, 1, 3, false>::mTensorMap = { { 0, 5, 4}, {8, 1, 3}, {7, 6, 2} };
The problem is that I have to do this for every order (1, 2, 3, and 4). And should I have Tensors of other types (here, real is a typdef for long double), I will have too much duplicate code.
Where can I initialize the maps then?