Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class template specializations with shared functionality

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?

like image 721
Thomas Avatar asked May 03 '10 11:05

Thomas


People also ask

What is function template specialization?

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.

What is the specialty of a template function give example?

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.

What is a template class what is the advantage of using a template class?

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.

How can you differentiate a function template from a class template?

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.


2 Answers

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 !!
}
like image 168
Rexxar Avatar answered Sep 20 '22 06:09

Rexxar


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.

like image 38
James McNellis Avatar answered Sep 21 '22 06:09

James McNellis