Skeen
Skeen

Reputation: 4722

How can I make operators on subclasses return the subclass type?

I'm in the progress of converting a piece of code from one geometric library to another. In my current code, I have a lot of specialized Point classes, and I'd rather have a single templated one. The new templated one, has an overloaded addition operator, defined as;

template<typename To>
Point<T, N> operator+(const Point<To,N>& p) const;

Where T is the contained type, and N is the dimensional arity of the point.

In order to provide the old usage interface, I had to subclass the Point class. Mainly by aliasing some members, and adding a few functions;

// Old library, could be indexed by var.x, ...
// New library, can only be indexed via. var[i]
int& x = data[0];
int& y = data[1];
int& z = data[2];

// Old library, supplied this function, new one didn't.
void subclass_function();

This works like a charm, and this replacement does it's job in most call-sites throughout the program. That is, unless there's an arithmetic operation, followed by a function provided by the compatibility class, say:

IntPoint3 index;
// The scalar multiplication results in the Point<int, 3>.
(index * 2).subclass_function();

Error: The Point<int, 3> does not have the subclass_function() defined.

What is the suggested solution to this? - (i.e. get the operator to return the subclass type)

Note1: I'd rather edit the Point class, than wrap all overloads of the arithmetic operators, in each specialized subclass.

Note2: The specialized subclasses doesn't add state.

Upvotes: 3

Views: 269

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275585

Use the curiously repeating template pattern (CRTP).

The base template class Point_impl takes its derived type as an argument.

It returns the derived type from its operators.

template<class T, unsigned N, class D>
struct Point_impl {
  D* self() { return static_cast<D*>(this); }
  D const* self() const { return static_cast<D const*>(this); }

  template<typename U, typename D2>
  D operator+(Point_impl<U,N,D2> const& o) const {
    auto retval = *self();
    retval += o;
    return retval;
  }
};

Then your derived:

struct Bob:Point_impl<int,3,Bob>{
  //...
};

etc.

I find static_assert in self with is_base_of wise as well, as it catches some typos.

Upvotes: 1

Related Questions