I'm writing a simple maths library with a template vector type:
template<typename T, size_t N>
class Vector {
public:
Vector<T, N> &operator+=(Vector<T, N> const &other);
// ... more operators, functions ...
};
Now I want some additional functionality specifically for some of these. Let's say I want functions x()
and y()
on Vector<T, 2>
to access particular coordinates. I could create a partial specialization for this:
template<typename T>
class Vector<T, 3> {
public:
Vector<T, 3> &operator+=(Vector<T, 3> const &other);
// ... and again all the operators and functions ...
T x() const;
T y() const;
};
But now I'm repeating everything that already existed in the generic template.
I could also use inheritance. Renaming the generic template to VectorBase
, I could do this:
template<typename T, size_t N>
class Vector : public VectorBase<T, N> {
};
template<typename T>
class Vector<T, 3> : public VectorBase<T, 3> {
public:
T x() const;
T y() const;
};
However, now the problem is that all operators are defined on VectorBase
, so they return VectorBase
instances. These cannot be assigned to Vector
variables:
Vector<float, 3> v;
Vector<float, 3> w;
w = 5 * v; // error: no conversion from VectorBase<float, 3> to Vector<float, 3>
I could give Vector
an implicit conversion constructor to make this possible:
template<typename T, size_t N>
class Vector : public VectorBase<T, N> {
public:
Vector(VectorBase<T, N> const &other);
};
However, now I'm converting from Vector
to VectorBase
and back again. Even though the types are the same in memory, and the compiler might optimize all this away, it feels clunky and I don't really like to have potential run-time overhead for what is essentially a compile-time problem.
Is there any other way to solve this?
The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.
Template in C++is a feature. We write code once and use it for any data type including user defined data types. For example, sort() can be written and used to sort any data type items. A class stack can be created that can be used as a stack of any data type.
Definition. As per the standard definition, a template class in C++ is a class that allows the programmer to operate with generic data types. This allows the class to be used on many different data types as per the requirements without the need of being re-written for each type.
For normal code, you would use a class template when you want to create a class that is parameterised by a type, and a function template when you want to create a function that can operate on many different types.
I think you can use CRTP to solve this problem. This idiom is used in boost::operator.
template<typename ChildT, typename T, int N>
class VectorBase
{
public:
/* use static_cast if necessary as we know that 'ChildT' is a 'VectorBase' */
friend ChildT operator*(double lhs, ChildT const &rhs) { /* */ }
friend ChildT operator*(ChildT const &lhs, double rhs) { /* */ }
};
template<typename T, size_t N>
class Vector : public VectorBase<Vector<T,N>, T, N>
{
};
template<typename T>
class Vector<T, 3> : public VectorBase<Vector<T, 3>, T, 3>
{
public:
T x() const {}
T y() const {}
};
void test()
{
Vector<float, 3> v;
Vector<float, 3> w;
w = 5 * v;
w = v * 5;
v.x();
Vector<float, 5> y;
Vector<float, 5> z;
y = 5 * z;
y = z * 5;
//z.x(); // Error !!
}
Here's something I came up with when playing with C++0x features a while back. The only C++0x feature used in this is static_assert
, so you could use Boost to replace that.
Basically, we can use a static size check function that just checks to be sure a given index is less than the size of the vector. We use a static assert to generate a compiler error if the index is out of bounds:
template <std::size_t Index>
void size_check_lt() const
{
static_assert(Index < N, "the index is not within the range of the vector");
}
Then we can provide a get()
method that returns a reference to the element at a given index (obviously a const overload would be useful too):
template <std::size_t Index>
T& get()
{
size_check_lt<Index>(); return data_[Index];
}
Then we can write simple accessors like so:
T& x() { return get<0>(); }
T& y() { return get<1>(); }
T& z() { return get<2>(); }
If the vector has only two elements, you can use x and y but not z. If the vector has three or more elements you can use all three.
I ended up doing the same thing for constructors--I created constructors for vectors of dimension two, three, and four and added a size_check_eq
that allowed them to be instantiated only for vectors of dimension two, three, and four, respectively. I can try and post the complete code when I get home tonight, if anyone is interested.
I dropped the project halfway through, so there might be some huge problem with doing it this way that I didn't run into... at least it's an option to consider.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With