C++: Пользовательский тип данных — проблемы с приведением типов и объединением

Что я пытаюсь сделать, так это создать новый пользовательский тип данных, который ведет себя как все другие примитивные типы. В частности, этот тип данных выглядит как дробь с фиксированной точкой. Я создал класс для представления этого типа данных, называемый "класс FixedPoint", и в нем есть способы приведения типов из "FixedPoint" в "int", "double" или "unsigned int" и т. д. Это нормально.

А что, если я хочу использовать "int" to "FixedPoint"? Первоначально мое решение состояло в том, чтобы иметь конструктор:

FixedPoint(int i) { /* some conversion from 'int' to 'FixedPoint' in here */ }

Это работает... но вы не можете поместить его в союз следующим образом:

 union {
   FixedPoint p;
 };

Это сломается, потому что "FixedPoint" не имеет неявного тривиального конструктора (мы только что определили конструктор "FixedPoint(int i)").

Подводя итог, вся проблема заключается в том, что «мы хотим cast from some type T to type FixedPoint without explicitly defining a constructor, чтобы мы могли использовать наш тип FixedPoint в объединении».

Что я думаю о решении, но не могу найти никаких доказательств в Интернете: определить перегруженный глобальный оператор приведения типов для приведения от «int» к «FixedPoint».

Есть ли способ сделать это без использования конструкторов классов? Я хотел бы иметь возможность использовать этот класс в союзе. Что я пробовал (в глобальном масштабе):

operator (FixedPoint f, int a) { ... } //compiler complains about this is not a method or non-static.

И небольшой пример, чтобы показать, что объединениям не нравятся конструкторы (им нравится POD)

class bob
{
  public:
    bob(int a) { m_num = a; }
  private:
    int m_num;
};
void duck()
{
  union 
  {
    bob a;
  };
}

Эта ошибка, наблюдаемая в Visual Studio:

error C2620: member 'duck::<unnamed-tag>::a' of union 'duck::<unnamed-tag>' has user-defined constructor or non-trivial default constructor

Любые идеи? Спасибо


person Ootawata    schedule 28.09.2010    source источник


Ответы (3)


Мне трудно понять, для чего вы попытаетесь использовать это. Кажется вонючим постоянно следить за тем, чтобы sizeof(FixedPoint) == sizeof(int) и, предполагая, что есть другие скрытые ошибки, такие как порядок следования байтов. Может быть, мне следует сделать небольшую резервную копию здесь, союзы только «преобразуют» значение из одного типа в другой, поскольку он занимает кусок памяти и ссылается на него как на другой тип. то есть

union BadConverter
{
    int integer;
    double fraction;
};

BadConverter.fraction = 100.0/33.0;
BadConverter.integer = ?;

Я почти уверен, что целое число не будет равно 3, оно будет равно тому фрагменту памяти двойного числа, который разделяет с ним целочисленные байты.

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

class FixedPoint
{
    FixedPoint& operator=(int value);
    FixedPoint& operator=(double value);
    ..etc..
    //Maybe something like this?
    template<typename T>
    FixedPoint& operator=(const T& value)
    {
        value = boost::lexical_cast<int>(value);
        return *this;
    }
}
person messenger    schedule 28.09.2010
comment
Да... На данный момент мы превратили все объединения в структуры в качестве хака (для всего, что использует фиксированную точку) - person Ootawata; 29.09.2010
comment
@Ootawata: Если вас устраивает ответ, вы должны принять его. - person messenger; 29.09.2010
comment
Чтобы действительно завершить это, я сделал кучу глобальных функций, которые все принимали разные типы (int, double, float, short и т. д.), каждая из которых возвращала бы тип FixedPoint. Невозможно привести тип к фиксированной точке — вы должны вызвать одну из глобальных функций. Это позволило мне сохранить класс как обычный старый тип данных для использования в объединениях. - person Ootawata; 09.05.2012

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

РЕДАКТИРОВАТЬ: я переработал пример, чтобы использовать union, так как это то, о чем вы спрашивали.

EDIT2: см. ниже, если вы пытаетесь пойти другим путем (строительство) и не хотите конструкторов.

#include <string>
#include <sstream>
using namespace std;

class FixedPoint
{
public:
    operator std::string() const
    {
        stringstream ss;
        ss << x_ << ", " << y_;
        return ss.str();
    }
    int x_, y_;
};

union Items
{
    FixedPoint point_;
    int val_;
};

int main()
{
    Items i;
    i.point_.x_ = 42;
    i.point_.y_ = 84;
    string s = i.point_;
}

Если вы пытаетесь пойти другим путем — например, от int к FixedPoint в моем примере — то нормальный способ сделать это — использовать конструктор преобразования. Учитывая, что вам не нужен нетривиальный конструктор, вам придется прибегнуть к функции преобразования.

FixedPoint make_fixed_point(int x, int y)
{
    FixedPoint ret;
    ret.x_ = x;
    ret.y_ = y;
    return ret;
}

union Items
{
    FixedPoint point_;
    int val_;
};


int main()
{
    Items i;
    i.point_ = make_fixed_point(111,222);
}
person John Dibling    schedule 28.09.2010
comment
Я думаю, что ОП спрашивал о конструкторах преобразования, а не об операторах преобразования, то есть он хочет, чтобы xy = ‹type› не ‹type› = xy - person Eugen Constantin Dinca; 29.09.2010

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

Например:

class bob
{
  public:
    bob(int a=0) { m_num = a; }
  private:
    int m_num;
};
void duck()
{
  union 
  {
    bob a;
  };
}

Указав значение по умолчанию a=0, его можно будет поместить в объединение. Хотя сам не пробовал.

person miked    schedule 28.09.2010
comment
У вас не может быть объектов с нетривиальными конструкторами в объединениях. Может, стоило попробовать самому? - person John Dibling; 29.09.2010