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?
What you ultimately want, i think, is to have the user type
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:
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
Vectorthrough pointers toVectorBase(normally they should not even be able to nameVectorBase: Put those bases in some implementation namespace, likedetail).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(usingboost::enable_if) to enable or disable them for particular values ofN, or use a int-to-type transformer like this which is much simplierThat 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).