c++inheritancespecialization

Inheritance vs Specialization


Considering the following two usage scenarios (exactly as you see them, that is, the end-user will only be interested in using Vector2_t and Vector3_t):

[1]Inheritance:

template<typename T, size_t N> struct VectorBase
{
};

template<typename T> struct Vector2 : VectorBase<T, 2>
{
};

template<typename T> struct Vector3 : VectorBase<T, 3>
{
};

typedef Vector2<float> Vector2_t;
typedef Vector3<float> Vector3_t;

[2]Specialization:

template<typename T, size_t N> struct Vector
{
};

template<typename T> struct Vector<T, 2>
{
};

template<typename T> struct Vector<T, 3>
{
};

typedef Vector<float, 2> Vector2_t;
typedef Vector<float, 3> Vector3_t;

I can't make up my mind as to which is a nicer solution. The obvious advantage to inheritance is code reuse in the derived classes; a possible disadvantage being performance (bigger size, users may pass by value, etc). Specialization seems to avoid all that, but at the expense of me having to repeat myself multiple times.

What other advantages/disadvantages did I miss, and in your opinion, which route should I take?


Solution

  • What you ultimately want, i think, is to have the user type

    Vector<T, N>
    

    And depending on N, the user will get slight different things. The first will not fulfill that, but the second will, on the price of code duplication.

    What you can do is to invert the inheritance:

    template<typename T, size_t N> struct VectorBase 
    {
    };
    
    template<typename T> struct VectorBase<T, 2>
    {
    };
    
    template<typename T> struct VectorBase<T, 3>
    {
    };
    
    template<typename T, size_t N> struct Vector : VectorBase<T, N>
    {
    };
    

    And implement the few functions that depend only on N being some specific value in the appropriate base-class. You may add a protected destructor into them, to prevent users deleting instances of Vector through pointers to VectorBase (normally they should not even be able to name VectorBase: Put those bases in some implementation namespace, like detail).

    Another idea is to combine this solution with the one mentioned in another answer. Inherit privately (instead of publicly as above) and add wrapper functions into the derived class that call the implementations of the base-class.

    Yet another idea is to use just one class and then enable_if (using boost::enable_if) to enable or disable them for particular values of N, or use a int-to-type transformer like this which is much simplier

    struct anyi { };
    template<size_t N> struct i2t : anyi { };
    
    template<typename T, size_t N> struct Vector
    {
        // forward to the "real" function
        void some_special_function() { some_special_function(i2t<N>()); }
    
    private:
        // case for N == 2
        void some_special_function(i2t<2>) {
            ...
        }
    
        // case for N == 3
        void some_special_function(i2t<3>) {
            ...
        }
    
        // general case
        void some_special_function(anyi) {
            ...
        }
    };
    

    That way, it is completely transparent to the user of Vector. It also won't add any space overhead for compilers doing the empty base class optimization (quite common).