Reputation: 17131
Given the following classes:
template <typename DataType, size_t Dimensions>
class Vector : public std::array<DataType, Dimensions> {
//stuff
};
template <typename DataType>
class Vector2 : public Vector<DataType, 2> {
//2d specific stuff
};
template <typename DataType, size_t Dimensions>
class Line {
public:
Vector<DataType, Dimensions>& min();
Vector<DataType, Dimensions>& max();
private:
Vector<DataType, Dimensions> m_min;
Vector<DataType, Dimensions> m_max;
};
template <typename DataType>
class Line2 : public Line<DataType, 2> {
//2d specific stuff
};
What's the best way to have min()
and max()
when called on a Line2
, return a Vector2&
rather than a Vector&
? Can I promote m_min
and m_max
to Vector2
within Line2
? Or otherwise override them and still have the Line
base class function properly?
Upvotes: 0
Views: 311
Reputation: 17131
A bit of an update, this is what I wound up doing:
Because some of the functions that would have belonged in the Base class needed to return a copy of the Vector, I would have needed to use CRTP. But I really didn't like the skeleton code required for that. It was overly complex.
template <typename derived>
struct test_base {
derived baz() { return *static_cast<derived *>(this); }
};
template <int N>
struct test : public test_base<test<N>> {
};
template <>
struct test<2> : public test_base<test<2>> {
test bar() { return *this; }
};
int main() {
test<1> a = {};
test<2> b = {};
auto c = a.baz();
auto d = b.baz();
auto e = b.bar();
return 0;
}
So in a desire to seek a flatter hierarchy I resorted to some template trickery:
#include <type_traits>
template <int N>
struct test {
void foo() {}
template <int P=N>
typename std::enable_if<P == 2 && P == N>::type bar() {}
template <int P=N>
typename std::enable_if<P == 3 && P == N>::type baz() {}
};
int main() {
test<1> a = {};
test<2> b = {};
test<3> c = {};
a.foo();
b.foo();
c.foo();
b.bar();
c.baz();
return 0;
}
Which seemed to me to be a cleaner solution. And it also allows me to write functions like:
template <int P=N>
typename std::enable_if<P >= 2 && P == N>::type x() { return Base::operator[](0); }
Upvotes: 0
Reputation: 6706
I think that Kerrek's suggestion of using partial template specialization together with a common base class is sensible, but you should apply the technique to the Vector type:
template <typename DataType, size_t Dimensions>
class VectorBase : public std::array<DataType, Dimensions> {
// things common to all vectors here
};
template <typename DataType, size_t Dimensions>
class Vector : public VectorBase<DataType, Dimensions> {
// nothing here
};
template <typename DataType>
class Vector<DataType, 2> : public VectorBase<DataType, Dimension> {
// 2d specific stuff here, so for example:
DataType& x() { return at(0); }
DataType& y() { return at(1); }
};
template <typename DataType, size_t Dimensions>
class Line {
public:
Vector<DataType, Dimensions>& min();
Vector<DataType, Dimensions>& max();
private:
Vector<DataType, Dimensions> m_min;
Vector<DataType, Dimensions> m_max;
};
Now you can do:
Line<double, 2> myLine;
double foo = myLine.max().x();
You could also apply the technique to the Line class, but that would only be useful to add functions that are specific to 2D lines, such as perhaps computing a Voronoi diagram. You do not need any Line specialization to have Line return a 2D Vector -- that happens automatically.
Upvotes: 1
Reputation: 476990
The usual approach is to break the template down into common and special parts:
template <typename T, size_t N> struct LineCommon { /* ... */ };
template <typename T, size_t N> struct Line : LineCommon<T, N>
{
Vector<T, N> & min();
Vector<T, N> & max();
};
template <typename T> struct Line2 : LineCommon<T, 2>
{
Vector2<T> & min();
Vector2<T> & max();
};
Upvotes: 1