Search code examples
c++staticconstantspartial-specializationtemplate-classes

partial specialization of a static const member variable


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?


Solution

  • I was able to go off the other answer and came up with the following solution. In the Tensor header file, I only declare the static const in the class definition. Afterwards, I template the member initialization using a MapCreator class.

    //Tensor.hpp
    template< typename T, int Order, int Dim, bool Sym >
    class space::Tensor
    {
    protected:
        Mat< T> mStorageMat;
        static const Mat < unsigned int > mTensorMap;
    public:
        // ...
    };
    
    template< typename T, int Order, int Dim, bool Sym >
    const space::Mat< unsigned int> space::Tensor<T, Order, Dim, Sym>::mTensorMatp = space::TensorMapCreator< Dim, Sym>::makeMap();
    

    Then, the TensorMapCreator class is only templated for the two arguments that my member variable depends on:

    //TensorMapCreator.hpp
    namespace space {
        template< int Dim, bool Sym>
        class TensorMapCreator;
    
        class TensorMapCreator< 2, true >; // specialized forward
        class TensorMapCreator< 2, false>; // declarations
        class TensorMapCreator< 3, true >;
        class TensorMapCreator< 3, false>;
    }
    
    class space::TensorMapCreator< 2, true >
    {
    public:
        static
        space::Mat< unsigned int>
        makeMap()
        {
            // creates the correct map
        }
    };
    // 3 more specializations for the other
    // combinations of dimmension and symmetry