У меня есть этот класс для факторизации общих операций на N-мерных векторных пространствах:
template <unsigned int N>
struct BaseVector
{
float m_data[N];
// Common operations like dot product, magnitude, test for unity, etc.
};
Примечание: я действительно хочу разложить как можно больше кода, чтобы минимизировать объем документации и тестирования.
Теперь я извлекаю два класса:
// 3D geometric vectors
struct Vector3 : public BaseVector<3>
{
Vector3 Cross(const Vector3& other);
// ...
};
// Quaternions are a particular kind of vector space
struct Quaternion : public BaseVector<4>
{
void Interpolate(const Quaternion& start, const Quaternion& end, ...);
// ...
};
Эти классы ведут себя одинаково для сложения и скалярного умножения (компонентная операция); Итак, я хочу факторизовать operator+=()
а также operator*=()
в базовом классе векторов.
Мой вопрос: как я могу вернуть ссылку правильного типа?
template <unsigned int N>
struct BaseVector
{
??? & operator+=(const BaseVector& other)
{
transform(m_data, m_data+N, other.m_data, m_data, plus<float>());
return ???
}
};
Все мои идеи (перечисленные ниже) не удовлетворяют, и я был бы признателен за некоторые предложения, спасибо!
Идея № 1: использовать C ++ ковариантный механизм возвращаемого типа. Но тогда я должен перегрузить эти операторы в производных классах — я прав? (Что для меня означает повторное тестирование.)
Идея № 2: перейти на шаблоны?
template <unsigned int N>
struct BaseVector
{
template <typename T2>
T2 & operator+=(const T2& other)
{
transform(...);
return *this; // THIS IS WRONG! I'm trying to "upcast"}
};
Идея № 3: Факторизовать код в закрытый член в базовом векторе, но затем мне нужно добавить больше функций в производные классы (и больше материала для тестирования)
template <unsigned int N>
struct BaseVector
{
private:
void DoOperatorPlus(const BaseVector& other) { transform(...); }
};
struct Vector4 : public BaseVector<4>
{
Vector4& operator+=(const Vector4& other)
{
DoOperatorPlus(other);
return *this;
}
};
Вы могли бы на самом деле попытаться использовать CRTP
Идея состоит в том, что вы передаете параметр шаблона вашему базовому классу вашего производного класса:
template <unsigned int N, typename Derived>
struct BaseVector
{
Derived & operator+=(const Derived& other)
{
transform(m_data, m_data+N, other.m_data, m_data, plus<float>());
return static_cast<Derived&>(*this);
}
};
Я не уверен на 100% насчет возврата, но это должно дать вам представление.
Других решений пока нет …