Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
200 views
in Technique[技术] by (71.8m points)

c++ - 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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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 !!
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...