псевдоним C++ для элемента массива

у меня есть (столбец) векторный класс, который содержит массив значений, к которым можно получить доступ, например:

Vec<int, 4> v();
v[0] = -2; // <- set first value to -2
v[1] = 1; // <- set second value to 1
....

но вот мой вопрос: Как создать псевдоним для v[0], v[1], v[2], v[3]?. Я хотел бы определить первые 4 значения а v.x, v.y, v.z, v.w:

Vec<int, 4> v();
v.x = -2; // <- set first value to -2
v.y = 1; // <- set second value to 1
v.z = 4; // <- set third value to 4
v.w = 2; // <- set fourth value to 2

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

Vec<int, 4> v();
v.x() = -2; // <- set first value to -2

ничего хорошего. На кроме того, векторный класс шаблонен, и x должен быть определен только для dimensions >= 1 и y только для dimenions >= 2 ... и так далее... Как мне этого достичь?

Edit: класс Vector не имеет ничего общего с std:: vector, это математический вектор, напоминающий массив, в том, что он имеет фиксированный размер и используется только для математических операций. (переименование вектора в Vec).

что я пробовал:

Матрица класс:

template <typename T, size_t ROWS, size_t COLS>
class Matrix {

    public:
        T& operator[] (size_t idx) {return m_matrix[idx];}
        T operator[] (size_t idx) const {return m_matrix[idx];}

    private:
        m_matrix[ROWS * COLS]
};

векторная класс:

template <typename T, size_t N>
class Vec: public Matrix<T, 1, N>{

    public:

        T& x() {return (*this)[0];}
        T x() const {return (*this)[0];}

        T& y() {return (*this)[1];}
        T y() const {return (*this)[1];}

        T& z() {return (*this)[2];}
        T z() const {return (*this)[2];}

        T& w() {return (*this)[3];}
        T w() const {return (*this)[3];}
};

это работает, и я легко могу использовать enable_if для удаления функций, если он не определен для этого измерения, это, однако, не синтаксически приятно. Ive попытался использовать ссылки:

template <typename T, size_t N>
class Vec: public Matrix<T, N, 1>{

    public:

        T& x = (*this)[0];
        T& y = (*this)[1];
        T& z = (*this)[2];
        T& w = (*this)[3];
};

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

Edit nr 2: там может просто существовать еще более простое решение, когда моя последняя попытка со ссылками компилируется с помощью компилятора Visual Studio community 2015 по умолчанию, тогда он работает. Но когда я компилирую его в Code:: Blocks с помощью компилятора GNU GCC, тогда это не так. Что говорится в стандарте? Разрешено ли мое решение использовать ссылки, какой компилятор неверен?

3 ответов


вы могли бы сделать что-то вроде:

// General case uses an array
template<class T, std::size_t N>
class VectorData
{
private:
    T m_data[N];

public:
    T& operator[](int i) { return m_data[i]; }
    const T& operator[](int i) const { return m_data[i]; }
};

// Specializations for various N (4 shown here)
template<class T>
class VectorData<T, 4>
{
public:
    T x, y, z, w;

    T& operator[](int i) { return (&x)[i]; }  // WARNING, see note below
    const T& operator[](int i) const { return (&x)[i]; }
};


template<class T, std::size_t N>
struct Vector : public VectorData<T, N>
{
    // your other Vector stuff here
};

Примечание: как правильно указал один из комментаторов ниже, это предполагает, что элементы массива выложены в памяти точно так же, как список переменных (iow,T[4] и struct { T x,y,z,w; } совместимы с макетом) для &x[i] часть работы. Стандарт не гарантирует этого, и поэтому этот код будет давать неопределенное поведение. На практике это нормально, и делать это таким образом намного эффективнее. Если вам нужно портативная, стандартная совместимая реализация, вы можете выбрать использование switch внутри VectorData::operator[], как подсказал другой ответ. Разница в генерируемом коде можно увидеть здесь.

Если вам действительно нужен вектор для получения из Матрицы, что-то вроде этого все еще возможно. Общая идея заключается в том, что вы отделяете хранилище и функциональность. Вы можете создать некоторый общий класс Матрицы со всеми функциями, который имеет дополнительный параметр шаблона для его хранения. Ля Vector может просто предоставить свой собственный тип хранения.

что-то типа:

// Generic matrix storage
template<class T, std::size_t N>
class MatrixData
{
private:
    T m_data[N];

public:
    T& operator[](int i) { return m_data[i]; }
    const T& operator[](int i) const { return m_data[i]; }
};

// Generic matrix class
template<class T, std::size_t ROWS, std::size_t COLS, class Storage = MatrixData<T, ROWS*COLS>>
class Matrix : public Storage
{
    // Matrix functionality here
};



// Specialized storage for Vectors, generic version
template<class T, std::size_t N>
class VectorData : public MatrixData<T, N> { };

// Specialized storage for Vector<T, 4>
template<class T>
class VectorData<T, 4>
{
public:
    T x, y, z, w;

    T& operator[](int i) { return (&x)[i]; }
    const T& operator[](int i) const { return (&x)[i]; }
};

template<class T, std::size_t N>
struct Vector : public Matrix<T, N, 1, VectorData<T, N>>
{
    // your other stuff here
};

это:

template <typename T, int D> struct Vec;

// You have to manually specialize for all needed sizes
template <typename T> struct Vec<T, 4>
{
    T x, y, z, w;

    T &operator[](int index)
    {
        switch (index)
        {
            default: // throw or something?
            case 0: return x;
            case 1: return y;
            case 2: return z;
            case 3: return w;
        }
    }
    const T &operator[](int index) const
    {
        switch (index)
        {
            default: // throw or something?
            case 0: return x;
            case 1: return y;
            case 2: return z;
            case 3: return w;
        }
    }
};

на switching по индексу не является оптимальным, но, по крайней мере, он хорошо определен.

для матриц я предпочитаю использовать Vec<Vec<T, Height>, Width>, которая составляет mat[x][y] нотация работы. (Swap x и y если вы хотите.)


если вы принимаете решение C++14, я предлагаю создать индексированную оболочку шаблона для x, y, z и w относительно T переменные

template <typename T, std::size_t>
struct wrapper
 { wrapper (T const &) {} };

template <typename T>
struct wrapper<T, 0U>
 { T & x; };

template <typename T>
struct wrapper<T, 1U>
 { T & y; };

template <typename T>
struct wrapper<T, 2U>
 { T & z; };

template <typename T>
struct wrapper<T, 3U>
 { T & w; };

рядом с std::array оболочку, которая должна быть унаследована до индексированные фантики

template <typename T, std::size_t N>
struct arrayWrp
 { std::array<T, N> arr {}; };

теперь вы можете определить помощника struct VecH следующим образом

template <typename T, std::size_t ... Is>
struct VecH<T, std::index_sequence<Is...>>
   : public arrayWrp<T, sizeof...(Is)>, public wrapper<T, Is>...
 {
   using arrayWrp<T, sizeof...(Is)>::arr;

   VecH () : arrayWrp<T, sizeof...(Is)>{}, wrapper<T, Is>{ arr[Is] }...
    { }

   T & operator[] (std::size_t i)
    { return arr[i]; }

   T const & operator[] (std::size_t i) const
    { return arr[i]; }
 };

, которые наследуют от arrayWrp и все wrapper<T, Is> и ссылки x, y, z и w to arr[0], arr[1], arr[2] и arr[3] rispectively

так Vec стать

template <typename T, std::size_t N>
struct Vec : public VecH<T, std::make_index_sequence<N>>
 { };

ниже приведен полный рабочий пример

#include <array>
#include <iostream>
#include <type_traits>

template <typename T, std::size_t>
struct wrapper
 { wrapper (T const &) {} };

template <typename T>
struct wrapper<T, 0U>
 { T & x; };

template <typename T>
struct wrapper<T, 1U>
 { T & y; };

template <typename T>
struct wrapper<T, 2U>
 { T & z; };

template <typename T>
struct wrapper<T, 3U>
 { T & w; };

template <typename T, std::size_t N>
struct arrayWrp
 { std::array<T, N> arr {}; };

template <typename, typename>
struct VecH;

template <typename T, std::size_t ... Is>
struct VecH<T, std::index_sequence<Is...>>
   : public arrayWrp<T, sizeof...(Is)>, public wrapper<T, Is>...
 {
   using arrayWrp<T, sizeof...(Is)>::arr;

   VecH () : arrayWrp<T, sizeof...(Is)>{}, wrapper<T, Is>{ arr[Is] }...
    { }

   T & operator[] (std::size_t i)
    { return arr[i]; }

   T const & operator[] (std::size_t i) const
    { return arr[i]; }
 };

template <typename T, std::size_t N>
struct Vec : public VecH<T, std::make_index_sequence<N>>
 { };

int main ()
 { 
   Vec<int, 4U>  v4;

   v4.x = 1;
   v4.y = 2;
   v4.z = 3;
   v4.w = 4;

   std::cout << "v4: ";

   for ( auto ui = 0U ; ui < 4U ; ++ui )
      std::cout << ' ' << v4[ui];

   std::cout << std::endl;

   Vec<int, 5U>  v5;  // also over 4

   Vec<int, 3U>  v3;

   v3.x = 10;
   v3.y = 20;
   v3.z = 30;
   // v3.w = 40;  // compilation error

 }

Если вам не нравится использование VecH помощник struct, вы можете использовать частичную специализацию и параметр шаблона по умолчанию std::make_index_sequence<N> следующим образом

template <typename, std::size_t N, typename = std::make_index_sequence<N>>
struct Vec;

template <typename T, std::size_t N, std::size_t ... Is>
struct Vec<T, N, std::index_sequence<Is...>>
   : public arrayWrp<T, N>, public wrapper<T, Is>...
 {
   using arrayWrp<T, sizeof...(Is)>::arr;

   Vec () : arrayWrp<T, sizeof...(Is)>{}, wrapper<T, Is>{ arr[Is] }...
    { }

   T & operator[] (std::size_t i)
    { return arr[i]; }

   T const & operator[] (std::size_t i) const
    { return arr[i]; }
 };

но я не знаю, хорошая ли это идея: кто-то может попытаться использовать Vec следующим образом

Vec<int, 3U, std::index_sequence<0, 2, 5>>  v;