2014-02-06 4 views
1

Я пытаюсь создать классы для математических векторов в разных измерениях, и поскольку они имеют много общего, я создал шаблонный базовый класс с размером векторов в качестве параметра шаблона. Я подклассифицирован, потому что мне нужны разные конструкторы (например, Vec2f(float x, float y), Vec3f(float x, float y, float z)) и дополнительные функции, такие как кросс-продукт для трехмерных векторов.Подкласс класса шаблона с операторами

Моя проблема: что должно operator+ в возврате базового элемента? Если он возвращает экземпляр базового слоя, то Vec3f + Vec3f возвращает Vecnf, но он должен вернуть Vec3f. Может ли это быть достигнуто каким-то образом?

Вот пример кода:

template <size_t n> 
class Vecnf { 
public: 
    Vecnf operator+(Vecnf const & vec) const { 
     return Vecnf(*this) += vec; 
    } 
    Vecnf & operator+=(Vecnf const & vec) { 
     for (int i = 0; i < n; ++i) { 
     elements[i] += vec.elements[i]; 
     } 
     return *this; 
    } 
protected: 
    std::array<float, n> elements; 
}; 

class Vec3f : public Vecnf<3> { 
public: 
    Vec3f(float x = 0.0f, float y = 0.0f, float z = 0.0f); 
    Vec3f crossProd(Vec3f const & vec); 
}; 

С этой реализации, следующее:

Vec3f a, b; 
Vec3f c = a + b; 

дает ошибку во время компиляции

error: conversion from 'Vecnf<3u>' to non-scalar type 'Vec3f' requested 

Я использую TDM GCC версии 4.8.1 на Windows 8 Pro. Я использую C++ 11, поэтому ваше решение также может использовать его, но, поскольку я не думаю, что это важно, я не задал вопрос с ним. Заранее спасибо :)

+2

CRTP поможет вам здесь. Я не уверен, что это было бы приятно. –

+0

Может быть [этот ответ] (http://stackoverflow.com/a/15666231) помогает? –

+0

'Vec3f' может иметь конструктор, который принимает' Vecnf <3> && '... – Jarod42

ответ

1

Вы могли бы сделать конструктор VARIADIC шаблон, тем самым решая свою первую проблему и избавляет от необходимости даже использовать наследование:

template <size_t n> 
class Vecnf 
{ 
    std::array<float, n> elements; 

public: 
    template <typename ... Args> 
    Vecnf(Args ... args): 
     elements {{static_cast<float>(args)...}} 
    {} 

    // other methods, operators etc... 
}; 

Теперь вы можете сделать typedef «S для часто используемые размеры, если вам нравится:

typedef Vecnf<3> Vec3f; 

на мой взгляд, вы могли бы даже улучшить, добавив еще один параметр шаблона для типа вы хотите сохранить в массиве. По умолчанию он может быть float.

Кроме того, оно не должно быть слишком трудно реализовать кросс-продукт в общем виде ...

+0

Или 'crossproduct' может быть свободной функцией, взявшей 2' vec3f'. – Jarod42

+0

Звучит здорово, но что происходит, когда я делаю 'Vec2f vec (1., 2., 3., 4.)'? Или дать ему меньше необходимого количества параметров для полной инициализации массива? Кроме того, происходит также солнечный ожог, поэтому я могу создать больше операторов трансляции. Меньшие векторы должны быть приведены к более крупным неявным образом, другой путь вокруг него должен происходить только явно, поскольку данные теряются. (Кросспродукт определяется только на трехмерных векторах, насколько мне известно, поэтому было бы хорошо объявить его свободной функцией). – Svensational

+0

@Svensational. Компилятор будет жаловаться, когда вы попытаетесь инициализировать его слишком большим количеством элементов. Когда вы предоставляете меньше элементов, чем размер вектора, остальные элементы будут инициализированы на 0. Это всего лишь поведение 'std :: array'. – JorenHeit

0

Вы можете просто определить оператор + на vec3f, что делает то, что вы хотите. Он может иметь различный тип возвращаемого значения для vecnf, если он является подтипом, и он все равно будет переопределять его.

Лучшее решение - это то, что представил @JorenHeit; это то, что вы хотели, вместе с двумя typedefs. Это решение совместимо с C++ 03, но его нет.

+0

Правда, но он заявил, что решение может использовать C++ 11. – JorenHeit

+0

@ JorenHeit Да, но не все будущие люди, читающие это, поймут это. Могут также быть и другие причины предпочтения подкласса, и знание о ковариантных типах возврата является хорошим. – dascandy

+0

Проблема в том, что я перепробовал несколько операторов ('+, + =, -, - =, *, * =, /,/=, []'). Код для них очень короткий, но он одинаковый для каждого векторного класса. На самом деле я мог вообще отказаться от шаблонов и подклассов и скопировать операторов в каждый класс (ведь для меня все это имеет смысл только для трех классов), но просто копируется код. Должен быть более элегантный способ;) – Svensational

Смежные вопросы