Проблема с перегруженным оператором Typecast для структуры внутри объединения - PullRequest
1 голос
/ 21 мая 2019

Я пытаюсь реализовать простую векторную функциональность в качестве любимого проекта, чтобы войти в метапрограммирование шаблонов.С помощью библиотеки математики с открытым исходным кодом glm и некоторых других публикаций по SO, я нашел решение, которое в основном работает, но имеет одну ошибку.

Я реализовал несколько структур, которые содержат нужные мне данныепредставлять двумерный евклидов вектор.Структура "vec2" имеет объединение, которое содержит массив float с двумя элементами (float data [2]) и двумя экземплярами структуры "scalarSwizzle", которая должна реализовывать механизм swizzling, который позволяет мне обращаться к вектору, как это происходит в vec.данные [0] или около того vec.x.

Следуя коду, который я реализовал до сих пор:

    #include <iostream>

    template<typename T>
    void print(T value)
    {
      std::cout << "print func: " << value << std::endl;
    }

    template<typename T, unsigned int I>
    struct scalarSwiz
    {
      T value[1];

      T &operator=(const T newValue)
      {
        value[I] = newValue;
        return value[I];
      }

      operator T()
      {
        return value[I];
      }
    };


    template<typename T>
    struct vec2
    {
      union
      {
        T data[2];
        scalarSwiz<T, 0> x;
        scalarSwiz<T, 1> y;
      };

      vec2()
      {
        x = 0.0f;
        y = 1.0f;
      }

      vec2(T pA, T pB)
      {
        x = pA;
        y = pB;
      }
    };


    int main(int argc, char *args[])
    {
     vec2<float> vec1{5.0f, 1.0f};

     std::cout << "value vec1.data[0]: " << vec1.data[0] << std::endl;
     std::cout << "value vec1.data[1]: " << vec1.data[1] << std::endl;
     std::cout << "value vec1.x: " << vec1.x << std::endl;
     std::cout << "value vec1.y: " << vec1.y << std::endl << std::endl;

     print(vec1.data[0]);
     print(vec1.data[1]);
     print(vec1.x);
     print(vec1.y);

     std::cin.get();

    }

Вывод следующий:

value vec1.data[0]: 5
value vec1.data[1]: 567.4
value vec1.x: 5
value vec1.y: 567.4

print func: 5
print func: 567.4
print func: 5
print func: 2.5565e-39

Я ожидалвыходные данные должны быть одинаковыми как для печати значений непосредственно в main (), так и через print (), но vec.y не разрешается, когда я печатаю его через функцию print ().Так что я думаю, что-то не так с перегруженным оператором Typecast в "scalarSwizzle", но я понятия не имею, что.Я также не понимаю, почему Visual Studio также не разрешает значение должным образом, как показано на следующем изображении:

Visual Studio Debugger

vec1.yуказывать на тот же физический адрес, что и на vec.x, в то время как direct std :: cout в main () работает нормально.

Я уже пару дней пытаюсь обернуть голову вокругпроблема, почему перегруженный оператор typecast не работает для vec.y, но я просто не понимаю.Может быть, кто-то здесь может помочь мне с этой проблемой.

Спасибо!

Ответы [ 2 ]

1 голос
/ 21 мая 2019

Прежде всего

template<typename T, unsigned int I>
struct scalarSwiz
{
  T value[1];

  T &operator=(const T newValue)
  {
    value[I] = newValue;
    return value[I];
  }

  operator T()
  {
    return value[I];
  }
};

приводит к неопределенному поведению, если I != 0 (доступ к массиву вне границ), поэтому не ожидайте, что ваш код будет правильным или даже стабильным.

Во-вторых, доступ к неактивному члену объединения также является неопределенным поведением (согласно стандарту c ++).Однако msvc, gcc и clang расширяют стандарт c ++, так что доступ к неактивному члену ведет себя так, как мы ожидаем.

И, наконец, ваш тип scalarSwiz может быть заменен анонимной структурой:

template<typename T>
struct vec2
{
  union
  {
    T data[2];
    struct
    {
       T x, y;
    };
  };

  vec2()
  {
    x = 0.0f;
    y = 1.0f;
  }

  vec2(T pA, T pB)
  {
    x = pA;
    y = pB;
  }
};

Относительно вашего дисплея отладчика Visual Studio: это из-за вашего определения scalarSwiz.Вы определяете массив длиной 1 T value[1] и помещаете 2 scalarSwiz объектов в объединение.Поскольку каждый член объединения совместно использует одну и ту же память (или, скорее, начинается в одной и той же ячейке памяти), оба ваших члена value указывают на начало массива data.Окно наблюдения отображает только элементы и их значения определенного типа, оно не знает о вашей необычной индексации.И поскольку оба массива занимают одну и ту же память, отображается одно и то же значение.

0 голосов
/ 21 мая 2019

Я обновил свой код относительно ответа Тимо:

    #include <iostream>

    template<typename T>
    void print(T value)
    {
      std::cout << "print func: " << value << std::endl;
    }

    template<typename T>
    struct vec2
    {
      union
      {
        T data[2];
        struct
        {
          T x, y;
        };
      };

      vec2()
      {
        x = 0.0f;
        y = 1.0f;
      }

      vec2(T pA, T pB)
      {
        x = pA;
        y = pB;
      }
    };


    int main(int argc, char *args[])
    {
     vec2<float> vec1{5.0f, 1.0f};

     std::cout << "value vec1.data[0]: " << vec1.data[0] << std::endl;
     std::cout << "value vec1.data[1]: " << vec1.data[1] << std::endl;
     std::cout << "value vec1.x: " << vec1.x << std::endl;
     std::cout << "value vec1.y: " << vec1.y << std::endl << std::endl;

     print(vec1.data[0]);
     print(vec1.data[1]);
     print(vec1.x);
     print(vec1.y);

     std::cin.get();

    }
...