2016-08-26 14 views
6

Jestem w trakcie tworzenia klasy wektorowej i próbuję znaleźć sposoby ponownego użycia maksymalnej ilości kodu dla wektorów o różnych rozmiarach. Oto prosty przykład:Przekazywanie tymczasowej struktury jako argumentu szablonu

template<typename T, unsigned int D> 
class Vector 
{ 
public: 
    union { 
     T v[D]; 
     struct { 
      /* T x; 
      * T y; 
      * T z; 
      * T w; 
      */ 
     }; 
    }; 

    Vector() 
    { 
     for(unsigned int i=0; i<D; ++i) 
      (*this)[i] = T(0); 
    } 
    Vector(T scalar) 
    { 
     for(unsigned int i=0; i<D; ++i) 
      (*this)[i] = scalar; 
    } 

    inline T operator[](int i) { return (*this).v[i]; } 
}; 

Chcę, zmienne składowe być publicznie dostępne. Ex:

Vector<float,2> vec; 
printf("X: %.2f, Y: %.2f\n", vec.x, vec.y); 

Co chciałbym zrobić coś na wzór tego:

template<typename T> 
class Vector2 : public Vector<T,2, struct { T x; T y; }> {}; 

template<typename T> 
class Vector3 : public Vector<T,2, struct { T x; T y; T z; }> {}; 

i go przesłonić struct w Unii:

template<typename T, unsigned int D, struct C> 
class Vector 
{ 
public: 
    union { 
     T v[D]; 
     // Place the passed struct here 
    }; 
}; 

tam jest jakikolwiek możliwy sposób to zrobić? Jeśli to możliwe, nie chcę używać niczego poza biblioteką standardową. Z góry dziękuję.

EDYCJA: Po przeczytaniu wszystkich odpowiedzi, zrozumiałem, że sposób w jaki używam związków jest nieprawidłowy! Dziękuję @ M.M za wskazanie tego. Od tego czasu wybrałem inną trasę, ale wybrałem odpowiedź, która najlepiej pasuje do tego, czego szukałem w tamtym czasie. Jeszcze raz dziękuję za wszystkie mile widziane odpowiedzi poniżej!

+1

czy rzeczywiście * * chcą przekazać struct do 'Vector' czy chcesz tylko zdefiniować wektor2/3/4, który daje wynik końcowy? Ta klasa nie wydaje się być bardzo przydatna dla rozmiarów> 6-8. Również nie chcesz 'T (0)' w twoim ctor, chcesz 'T {}' domyślnie go zainicjować. – kfsone

+3

Nie jestem do końca pewien, co robisz, ale pamiętaj, że w C++ można przeczytać tylko ostatnio przypisanego członka związku; na przykład nie wolno pisać do 'v', a następnie czytać z' x'. –

+1

Co powiedział @MM jest naprawdę ważne! Nie używaj 'union' do" konwertowania "wartości na inną. Spójrz na 'std :: tuple' i przeładuj' operator [] ', aby osiągnąć to, co chcesz zrobić. – Garf365

Odpowiedz

1

Jeśli ja zrozumiałem swój Głównym celem było zadeklarowanie kolejności pól odpowiadających elementom tablicy klasy szablonowej. Nie można tego zrobić bezpośrednio, ponieważ szablony nie akceptują typu wbudowanego jako parametru. Aby obejść problem może grać z parametrów szablonu typu non-powiązać niektóre etykiety do danego indeksu tablicy:

#include <cstdio> 
#include <unordered_map> 
#include <utility> 

struct Label { } x, y, z, w; 

template <Label&... labels> 
struct Pack { }; 

template <class, class> 
struct VectorParent; 

template <Label&... labels, size_t... Is> 
struct VectorParent<Pack<labels...>, std::index_sequence<Is...>> { 
    static std::unordered_map<Label *, size_t> label_map; 
}; 

template <Label&... labels, size_t... Is> 
std::unordered_map<Label *, size_t> VectorParent<Pack<labels...>, std::index_sequence<Is...>>::label_map = {{&labels, Is}...}; 

struct LabelNotFound { }; 

template <class T, size_t N, Label&... labels> 
struct Vector:VectorParent<Pack<labels...>, std::make_index_sequence<sizeof...(labels)>> { 
    static_assert(N == sizeof...(labels), 
     "the cound of labels should corespond to the number of elements of the vector"); 
    using VectorParent<Pack<labels...>, std::make_index_sequence<sizeof...(labels)>>::label_map; 
    T t[N]; 
    T &operator->*(Label& l) { 
     auto it = label_map.find(&l); 
     if (it == label_map.end()) 
     throw LabelNotFound{}; 
     return t[it->second]; 
    } 
}; 

int main() { 
    Vector<float,2,x,y> vec; 
    vec->*x = 10.0f; 
    printf("X: %.2f, Y: %.2f\n", vec->*x, vec->*y); // prints: X: 10.00, Y: 0.00 
    //vec->*w = 10.1f; //would throw an exception LabelNotFound 
} 
4

To, co próbujesz zrobić, jest niedozwolone.
Zresztą, można to zrobić:

template<typename T> 
struct S { T x; T y; }; 

template<typename T> 
class Vector2 : public Vector<T,2,S<T>> {}; 

albo to:

template<typename T> 
class Vector2 : public Vector<T,2,S> {}; 

W drugim przypadku, Vector można zdefiniować jako:

template<typename T, unsigned int D, template<typename> class S> 
class Vector { 
    using MyStruct = S<T>; 

    // ... 

    union { 
     T v[D]; 
     MyStruct myStruct; 
    }; 
}; 
+0

Gdzie jest typ nieklasowany? Co mają z tym wspólnego parametry szablonu non-type? – aschepler

+0

@aschepler W niektórych rybitwach masz rację. To nie jest typ klasy ani typ nieklasyczny.Usuwam pierwszą część odpowiedzi, i tak pozostawiam alternatywne rozwiązania. – skypjack

2

To nie skaluje się bardzo dobrze w dużej D, ale jeśli jesteś po prostu cztery do sześciu wariantów Wyobrażam sobie, można częściowego specjalizują klasę bazową:

#include <iostream> 

template<typename T, size_t D> 
struct VectorBase; 

template<typename T> 
struct VectorBase<T, 2> 
{ 
    constexpr VectorBase() : v{} {} 
    union { 
     T v[2]; 
     struct { T x, y; }; 
    }; 
}; 

template<typename T> 
struct VectorBase<T, 3> 
{ 
    constexpr VectorBase() : v{} {} 
    union { 
     T v[3]; 
     struct { T x, y, z; }; 
    }; 
}; 

template<typename T> 
struct VectorBase<T, 4> 
{ 
    constexpr VectorBase() : v{} {} 
    union { 
     T v[4]; 
     struct { T x, y, z, w; }; 
    }; 
}; 

template<typename T, size_t D> 
struct Vector : public VectorBase<T, D> 
{ 
    using VectorBase<T, D>::v; 
    using size_type = decltype(D); 
    using value_type = T; 

    constexpr Vector() : VectorBase<T,D>{} {} 
    constexpr Vector(T scalar) { 
     std::fill(std::begin(v), std::end(v), scalar); 
    } 

    constexpr T& operator[](size_type i) const noexcept { return v[i]; } 
    constexpr const T& operator[](size_type i) noexcept { return v[i]; } 

    constexpr size_type size() const noexcept { return D; } 

    constexpr T* data() noexcept { return &v[0]; } 
    constexpr const T* data() const noexcept { return &v[0]; } 
}; 

template<typename T> 
using Vector2 = Vector<T, 2>; 
template<typename T> 
using Vector3 = Vector<T, 3>; 
template<typename T> 
using Vector4 = Vector<T, 4>; 

int main() { 
    Vector3<int> v{1}; 

    std::cout << v[0] << ", " << v.z << "\n"; 
    return 0; 
} 

żywo demo: http://ideone.com/T3QHoq