C ++ псевдонимы переменных-членов? - PullRequest
12 голосов
/ 30 января 2009

Я почти уверен, что это возможно, потому что я уверен, что видел, как это было сделано. Я думаю, что это здорово, но я с радостью приму ответы в духе «это ужасная идея, потому что ____».

Скажем, у нас есть базовая структура.

struct vertex
{
    float x, y, z;
};

Теперь я хочу реализовать псевдонимы для этих переменных.

vertex pos;
vertex col;
vertex arr;

pos.x = 0.0f; pos.y = 0.5f; pos.z = 1.0f;
col.r = 0.0f; col.g = 0.5f; col.b = 1.0f;
arr[0] = 0.0f; arr[1] = 0.5f; arr[2] = 1.0f;

В идеале третий синтаксис был бы неотличим от массива. То есть, если бы я отправил arr в качестве ссылочного параметра в функцию, ожидающую массив чисел с плавающей запятой, в которые он будет хранить данные (например, многие функции OpenGL glGet), он бы работал нормально.

Что вы думаете? Возможный? Возможно, но глупо?

Ответы [ 12 ]

22 голосов
/ 30 января 2009

Что бы я сделал, это сделал бы аксессоры:

struct Vertex {
    float& r() { return values[0]; }
    float& g() { return values[1]; }
    float& b() { return values[2]; }

    float& x() { return values[0]; }
    float& y() { return values[1]; }
    float& z() { return values[2]; }

    float  operator [] (unsigned i) const { return this->values_[i]; }
    float& operator [] (unsigned i)       { return this->values_[i]; }
    operator float*() const { return this->values_; }

private:
    float[3] values_;
}
13 голосов
/ 30 января 2009

Безымянные вложенные структуры в объединении не являются стандартом C ++. Это, однако, должно работать:

struct Vertex
{
private:
   typedef float Vertex::* const vert[3];
   static const vert v;

public:
   typedef size_t size_type;
   float x, y, z;

   const float& operator[](size_type i) const {
      return this->*v[i];
   }

   float& operator[](size_type i) {
      return this->*v[i];
   }

};

const Vertex::vert Vertex::v = {&Vertex::x, &Vertex::y, &Vertex::z};

РЕДАКТИРОВАТЬ: немного больше информации. Структура использует массив из трех элементов указателя на данные для доступа к данным в перегруженных операторах [].

Строка "typedef float Vertex :: * const vert" означает, что vert является указателем на член с плавающей точкой структуры Vertex. [3] означает, что это массив из 3 из них. В перегруженном операторе [] этот массив индексируется, а указатель на элемент данных разыменовывается и возвращается значение.

Кроме того, этот метод должен работать независимо от проблем с упаковкой - компилятор может дополнять структуру Vertex, как ему нравится, и все равно будет работать нормально. У анонимного союза возникнут проблемы, если поплавки будут по-разному упакованы.

13 голосов
/ 30 января 2009

Использовать союз?

union vertex
{
    struct { float x, y, z; };
    struct { float r, g, b; };
    float arr[3];
};

Я бы не советовал - это приведет к путанице.


Добавлена ​​

Как отметил Адриан в своем ответе, этот союз с анонимными членами структуры не поддерживается ISO C ++. Он работает в GNU G ++ (с жалобами на то, что он не поддерживается при включении '-Wall -ansi -pedantic'). Это напоминает предварительно стандартные дни C (pre-K & R 1st Edn), когда имена элементов структуры должны были быть уникальными во всех структурах, и вы могли использовать сокращенные обозначения, чтобы получить смещение внутри структуры, и вы могли использовать имена членов из других типов структуры - форма анархии. К тому времени, когда я начал использовать C (давным-давно, но после K & R1), это уже историческое использование.

Обозначения, показанные с анонимными членами объединения (для двух структур), поддерживаются C11 (ISO / IEC 9899: 2011), но не более ранними версиями стандарта C. Раздел 9.5 ИСО / МЭК 14882: 2011 (C ++ 11) предусматривает анонимные объединения, но GNU g++ (4.9.1) не принимает код, показанный с помощью -pedantic, идентифицирующий "warning: ISO C++ prohibits anonymous structs [-Wpedantic]".

Поскольку идея приведет к путанице, меня не особо беспокоит, что она не стандартная; Я бы не стал использовать механизм для этой задачи (и я бы опасался использовать анонимные структуры в объединении, даже если это было бы полезно).


Была высказана озабоченность:

Три (x-y-z, r-g-b и массив) не обязательно совпадают.

Это союз из трех элементов; три элемента начинаются с одного адреса. Первые два являются структурами, содержащими 3 значения с плавающей точкой. Нет наследования и нет виртуальных функций для предоставления разных макетов и т. Д. Структуры будут выложены из трех смежных элементов (на практике, даже если стандарт допускает заполнение). Массив также начинается с того же адреса, и при условии отсутствия заполнения в структурах элементы перекрывают две структуры. Я действительно не вижу в этом проблемы.

3 голосов
/ 30 января 2009

Ссылки

template<typename T>
struct vertex {
    vertex() :
        r(data[0]), g(data[1]), b(data[2]),
        x(data[0]), y(data[1]), z(data[2])
    {
    }

    T *operator *() {
        return data;
    }

    const T *operator *() const {
        return data;
    }

    T data[3];
    T &r, &g, &b;
    T &x, &y, &z;
};
3 голосов
/ 30 января 2009

Вы можете получить это с профсоюзом, как уже упоминали другие. Перегрузка цвета и положения на одну и ту же структуру может быть не очень хорошей идеей (например, добавление двух цветов обычно означает, что вы хотите насыщать до 1,0, тогда как добавление векторов происходит линейно), но наложение плавающего [] поверх них, это совершенно нормально и хорошо принятый способ обмена данными с GL / DirectX / и т. д.

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

vector foo; 
foo.x = 1.0f;
return foo[0] + foo[1];
2 голосов
/ 24 марта 2015

Следующая структура будет иметь запрошенное поведение:

struct vertex
{
private:
    float data[3];
public:
    float &x, &y, &z;
    float &r, &g, &b;

    vertex() : x(data[0]), y(data[1]), z(data[2]), r(data[0]), g(data[1]), b(data[2]) {
    }

    float& operator [](int i) { 
        return data[i];
    }
};
2 голосов
/ 30 января 2009

Я не уверен, правильно ли я понял вопрос. Но похоже, что вам нужно перегрузить оператор [], чтобы предоставить массиву доступ к вашей структуре / классу. См. Пример, упомянутый здесь: Перегрузка оператора

2 голосов
/ 30 января 2009

Полагаю, вы можете использовать магию макросов, чтобы получить то, что вы хотите. Но это будет выглядеть ужасно. Почему вы хотите использовать одну и ту же структуру, вершину для 3 разных типов? Почему вы не можете определить класс для цвета? Также имейте в виду, что вершина и цвет не совпадают. Если вы измените что-то на вершину, это также повлияет на цвет, если у вас будет один и тот же класс для обоих.

1 голос
/ 30 января 2009

Плохая идея, по моему мнению, по крайней мере для приведенного примера: недостатком является то, что практически для любого решения этой проблемы вы, вероятно, сможете свободно назначать экземпляры "rgb" для / из "xyz" случаи, что, вероятно, редко разумно или правильно. то есть вы рискуете отказаться от некоторого полезного типа безопасности.

Лично для примера, который я привожу, я бы выделил подклассы типов rgb и xyz из базового boost::array<float,3> или аналогичного. Таким образом, оба они наследуют operator [], могут быть переданы функциям, ожидающим массивы, и переданы с большей безопасностью типов вещам, ожидающим цвета / координаты. Часто вы хотите рассматривать xyz или rgb как массив, но редко вы хотите рассматривать xyz как rgb или наоборот. (Массив rgb IS-A: ОК. xyz Массив IS-A: ОК. rgb IS-A xyz ???? Я так не думаю!)

Конечно, это означает, что доступ к x, y, z & r, g, b должен быть получателем доступа (переадресация на соответствующий operator[](...)), а не прямым к участнику. (Для этого вам понадобятся свойства C #).

0 голосов
/ 08 марта 2009

Просто предупреждение об использовании ссылочных членов, указывающих на значения членов. Вам необходимо определить конструктор копирования (и, возможно, также оператор присваивания), если вы когда-либо копируете такой объект (например, передаете его по значению). Конструктор копирования по умолчанию предоставит вам копию, чьи ссылочные элементы указывают на значения элементов исходного объекта, а не на элементы нового объекта. Это, конечно, не то, что вы хотите.

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

...