2015-09-02 2 views
1

Я хочу реализовать численный Vector, который имеет несколько числовых операций.Реализация универсального векторного класса

Я использовал шаблон для определения родовой версии вектора.

Но реализация должна отличаться от конкретной реализации, например, Vector3.

Например, + Операция будет выглядеть так.

Vector3 реализация

Vector3 operator+(const Vector3& vec3) const{ 
    return Vector3(data[0]+vec3.data[0], data[1]+vec3.data[1], data[2]+vec3.data[2]); 
} 

реализация шаблона

GenericVector operator+(const GenericVector<T,N>& vec) const{ 
    GenericVector temp = *this; 
    for(int i=0;i<N;i++) 
    { 
     temp.data[i] += vec.data[i]; 
    } 
    return temp; 
} 

Теперь, когда мы не можем знать, сколько data это было бы, мы не можем определить конструктор как Vector3(x,y,z). Вот почему я представил для цикла в + operator перегрузке.

Фактически, когда дело доходит до появления кодов, реализация Vector3 выглядит лучше. Но его нельзя масштабировать для разных векторов размеров.

Реализация шаблона выглядит менее приятнее, чем у Vector3. Но версия шаблона может легко масштабироваться до многих векторов размеров, делая это.

typedef GenericVector<double, 3> Vector3d; 
typedef GenericVector<double, 6> Vector6d; 

Я не привык к шаблону C++. Есть ли кто-нибудь, кто может указать проблему моей реализации или понимания?

Мой вопрос заключается в том, что в реализации + operator в шаблоне версии, является выбором введения для петли для размещения неизвестных целого Nправа или единственного выбор?

Для простой один вопрос,

Могу ли я избежать цикл в + operator?

Это часть моих кодов.

template <typename T, int N> 
class GenericVector{ 
protected: 
    T data[N]; 
public: 
    GenericVector() 
    { 
     for(int i=0;i<N;i++) 
     { 
      data[i] = static_cast<T>(0); 
     } 
    } 

    GenericVector operator+(const GenericVector<T,N>& vec) const{ 
     GenericVector temp = *this; 
     for(int i=0;i<N;i++) 
     { 
      temp.data[i] += vec.data[i]; 
     } 
     return temp; 
    } 

    //... 
}; 
+0

Итак, в чем вопрос? Разве это не работает или вы спрашиваете, правильно ли это сделать? – NathanOliver

+0

Вам не нужно инициализировать 'data' до' 0' в цикле. Все, что вам нужно, это 'GenericVector(): data() {}'. И вам не нужны параметры шаблона в параметре 'operator +'. Это будет делать: 'GenericVector operator + (const GenericVector & vec) const'. Кроме этого, все выглядит нормально. Но я понятия не имею, о чем вы спрашиваете. – juanchopanza

+0

Кроме того, вам, вероятно, следует реализовать «GenericVector & operator + = (const GenericVector &)» и использовать его в 'operator +'. – juanchopanza

ответ

3

Один подхода, чтобы избежать петель в различных операциях является не магазина массива, но использовать наследование и специализацию, чтобы иметь дело с различным числом аргументов. Что-то вдоль линий этого:

template <typename T, int Size> class GVector; 
template <typename T> 
class GVector<T, 0> { 
public: 
    GVector() {} 
    GVector& operator+= (GVector const&) { return *this; } 
    GVector operator+ (GVector const& other) const { 
     return GVector() += other; 
    } 
    // ... 
}; 
template <typename T, int Size> 
class GVector: public GVector<T, Size - 1> { 
    T value; 
public: 
    GVector(): GVector<T, Size - 1>(), value() {} 
    template <typename Head, typename... Tail> 
    GVector(T const& head, Tail const&... tail) 
     : GVector<T, Size - 1>(tail...) 
     , value(head) { 
    } 
    GVector& operator+= (GVector const& other) { 
     this->value += other.value; 
     this->GVector<T, Size - 1>::operator+= (other); 
     return *this; 
    } 
    GVector operator+ (GVector const& other) const { 
     return GVector(*this) += other; 
    } 
    // ... 
}; 
+0

Думаю, вам не хватает специализации для вектора размера 1. – SergeyA

+1

Не уверен, что это реальный выигрыш, ну, что угодно. –

+0

Спасибо, я пытаюсь понять ваш ответ. BTW, что такое '...' в конструкторе 'GVector'? Я никогда их не видел. –

1

Там вообще компромисс, делая код более универсальным: вы уменьшить дублирование кода, когда вы можете использовать ваш общий код с различными параметрами, но это обычно означает, что несколько более сложный код, чем не- а также ограничивает возможности для оптимизации. Более эффективные компиляторы и новые возможности C++ помогают уменьшить компромиссы, но они все еще существуют.

Чтобы ответить на ваш вопрос, да, можно создать общий векторный класс, который не использует петли for для итерации по элементам вектора. Использование циклов for является приемлемой реализацией, однако в моих экспериментах с generic vector class я решил не использовать их, потому что нашел с моим компилятором, что они не были полностью развернуты даже в оптимизированных сборках.

Примечание. Этот код в основном представляет собой эксперимент по исследованию компромиссов при построении универсального векторного класса. Я бы не стал пропагандировать использование такого дизайна в производстве.

C++ 11 variadic templates предложить решение вашей проблемы конструкторы, хотя есть некоторые вопросы, которые приходят вместе с ними:

template <typename T, size_t N> 
class Vector { 
public: 
    static const size_t dimension = N; 

    Vector() = default; 
    Vector(const Vector&) = default; 

    template <typename... Ts> 
    Vector(T t, Ts&&... ts) 
     : aw({t, std::forward<Ts>(ts)...}) { 
     static_assert(sizeof...(Ts) == N - 1, "Constructor must be passed N initializers."); 
    } 

private: 
    struct ArrayWrapper { 
     T e_[N]; 
    } aw; // ArrayWrapper lets us initialize in constructor initializer 
}; 

Они также могут быть использованы, чтобы избежать зацикливания в операторах:

template <size_t I, typename F, typename... Args> 
auto apply(F f, Args&&... args) { 
    return f(args.e(I)...); 
} 

template <typename F, size_t... Is, typename... Args> 
auto apply(F f, std::index_sequence<Is...>, Args&&... args) { 
    using vec = std::common_type_t<Args...>; 
    using resvec = Vector<decltype(apply<0>(f, args...)), vec::dimension>; 
    return resvec{apply<Is>(f, args...)...}; 
} 

template <typename F, typename... Args> 
inline auto memberwise(F f, const Args&... args) { 
    using vec = std::common_type_t<Args...>; 
    return apply(f, std::make_index_sequence<vec::dimension>{}, args...); 
} 

template <typename T, size_t N> 
inline Vector<T, N> operator+(const Vector<T, N>& a, const Vector<T, N>& b) { 
    return memberwise(std::plus<>{}, a, b); 
} 

В моих экспериментах с этим подходом я нашел, что это дает хороший код в оптимизированных сборках с моим компилятором, но он добавляет довольно много накладных расходов в не оптимизированных сборках, больше, чем использование цикла for.

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