OmnipotentEntity
OmnipotentEntity

Reputation: 17131

Inheritance, promote base class members

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

Answers (3)

OmnipotentEntity
OmnipotentEntity

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

Alex Flint
Alex Flint

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

Kerrek SB
Kerrek SB

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

Related Questions