Константная корректность для данных, обрабатываемых неконстантными классами

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

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

#include "Vector.h"

void DoSomething(float* a1, const float* a2)
{
  // some code

  Vector v1(a1[0], a1[1], a1[2]);
  Vector v2(a2[0], a2[1], a2[2]);
  v1.MakeEqual(v2);

  // more code
}

int main()
{
  float x1[3] = { 0, 0, 0 };
  float x2[3] = { 1, 1, 1 };
  DoSomething(x1, x2);
  return 0;
}

Класс Vector определен в заголовочном файле:

#pragma once
class Vector
{
public:
  Vector(float& _x, float& _y, float& _z) :x(_x), y(_y), z(_z)
  {}

  Vector(const float& _x, const float& _y, const float& _z) 
      :x(const_cast<float&>(_x)), y(const_cast<float&>(_y)), z(const_cast<float&>(_z)) //works, but looks not good
  {}

  ~Vector(){};

  void MakeEqual(const Vector& other)
  {
    x = other.x;
    y = other.y;
    z = other.z;
  }

private:
  float& x;
  float& y;
  float& z;
};

Мои данные определяются где-то снаружи (main-функция), а затем передаются как указатели на функцию DoSomething. Внутри этой функции векторные объекты обертываются вокруг данных, чтобы помочь выполнять некоторые типичные для вектора вещи (вращение, кросс-произведения и т. д.). Теперь я хочу быть корректным с константой и передавать данные, которые не предназначены для изменения, как const, но когда создается вектор v2, компилятор жалуется, что он cannot convert argument 1 from 'const float' to 'float &'. Я понимаю, что он хочет сказать, но как мне это решить? Векторный метод имеет смысл только тогда, когда я использую его на векторах, но конструктор вектора v2 не знает, что я на самом деле не манипулирую данными v2.

Один из способов решения этой проблемы показан в коде с const_cast во втором конструкторе, но у меня такое чувство, что это нехороший стиль, поскольку я отбрасываю свои законные константные данные. Другой способ — определить второй класс ConstVector, но это тоже кажется странным.

Я должен упомянуть, что из-за ограничений компилятора я привязан к стандарту C++03, поэтому, к сожалению, я не могу использовать слишком современные возможности языка. Также stl-контейнеры (вероятно) недоступны, так как я не могу контролировать, как, когда и где выделяется память.


person Indy625    schedule 07.06.2017    source источник
comment
ваш MakeEqual изменяет x,y,z, поэтому вы (можете) изменить значение.   -  person apple apple    schedule 07.06.2017
comment
хотя это не рекомендуется для этого случая, вы можете использовать что-то вроде class const_vector для использования с константными значениями.   -  person apple apple    schedule 07.06.2017


Ответы (1)


У тебя есть:

Vector(float& _x, float& _y, float& _z) :x(_x), y(_y), z(_z)

Это плохо работает. Например, вы не можете использовать:

Vector v1(0, 0, 0);

Кроме того, вы не можете использовать

Vector v1(a1[0], a1[1], a1[2]);

поскольку a1[0] оценивается как const float&, а не float&.

Это плохо спроектированный интерфейс. Измените аргументы на простые значения:

Vector(float _x, float _y, float _z) :x(_x), y(_y), z(_z)
person R Sahu    schedule 07.06.2017
comment
Я предполагаю, что OP хочет, чтобы Vectors действовали как оболочка (может изменить исходный массив), поэтому Vector::Vector(float,float,float) может не соответствовать потребностям OP (для v1). - person apple apple; 07.06.2017
comment
@appleapple, надеюсь, что нет. Использование временных объектов в стеке, таких как Vectors, не очень полезно для выполнения вычислений, на которые они надеются. Я подожду, пока ОП разъяснит. - person R Sahu; 07.06.2017
comment
@appleapple, ты прав. Я использую класс Vector только для определенных операций с существующими данными. После того, как я это сделал, я могу полностью игнорировать вектор, так как он не содержит данных сам по себе. Возможно, можно будет передать данные в виде значений, как предложено, и, в конце концов, скопировать результат векторной операции обратно в данные. Больше нагрузки на стек, но, вероятно, лучшее решение, чем мое текущее... - person Indy625; 08.06.2017