Вопрос о злоупотреблении шаблоном в C ++ - добавление поплавков с дополнительной информацией о типах - PullRequest
1 голос
/ 19 сентября 2009

У меня была идея, мотивированная некоторой документацией, которую я прочитал в учебном пособии по библиотеке повышения MTL.

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

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

struct Radian
{
  float rad;
}

struct Degree
{
  float deg;
}

Это все хорошо, за исключением того, что я могу сделать что-то вроде

func()
{
  Radian r;
  Degree d;

  r.rad = d.deg;
}

Было бы хорошо, если бы я мог пометить такое назначение как ошибку типа времени компиляции. Это привело меня к рассмотрению следующего:

struct Degree {};
struct Radian {};

template <class Data, class Unit>
struct Quantity
{
    Data val;

    Quantity<Data,Unit>() : val() {}
    explicit Quantity<Data,Unit>(Data v) : val(v) {}
};

typedef Quantity<float,Radian> Rad;
typedef Quantity<float,Degree> Deg;

Теперь эквивалентный код функции func (), использующий типы Rad и Deg, будет отмечать это присваивание как ошибку времени компиляции (и с явным набором, даже выполнение чего-то столь же простого, как Rad r = 2.0, считается ошибкой времени компиляции) .

Что мне действительно нужно, так это float, у которого есть это свойство дополнительных единиц, которое можно использовать для отлова логических ошибок во время компиляции (т. Е. С использованием степеней в функции, которая ожидает радианы), но для всех намерений и целей эти вещи плавает.

В качестве общего вопроса, что вы думаете об этом подходе? Я немного обеспокоен тем, что это неправильный способ достижения моей цели, но у него странная привлекательность. Кроме того, есть ли «черта» или что-то вроде проверки концепции буста, которую я могу использовать, чтобы определить, что Данные «плавающие». Наконец, есть ли способ унаследовать реализации операций по умолчанию, такие как «<<», чтобы мне не пришлось реализовывать их все вручную в классе «Количество»? </p>

Ответы [ 5 ]

6 голосов
/ 19 сентября 2009

Я полагаю, вы ищете что-то вроде Boost.Units . Я думаю, что это место для начала.

3 голосов
/ 19 сентября 2009

Обычная система типов уже делает это:

Radian sin(Radian const& val) { /* Do Stuff */ }
Degree sin(Degree const& val) { /* Do Stuff */ }

Поскольку градусы и радианы - это разные типы, вы не можете назначать их друг другу.

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

Сделайте Val приватным, и теперь они не смогут нанести никакого ущерба.

1 голос
/ 19 сентября 2009

C ++ способ сделать это - сделать 'deg' и 'rad' закрытыми, чтобы компилятор мог очень легко предотвратить выполненное вами присваивание.

Затем вы добавляете операторы присваивания:

struct Radian
{
private:
  float rad;

public:
  Radian &operator=(const Radian &r)
  {
    rad = r.rad;
    return *this;
  } 
  Radian &operator=(const Degree &d)
  {
    rad = d.deg * DegToRadFactor;
    return *this;
  } 
};

struct Degree
{
private:
  float deg;

public:
  Degree &operator=(const Degree &d)
  {
    deg = r.deg;
    return *this;
  } 
  Degree &operator=(const Radian &r)
  {
    deg = r.rad / DegToRadFactor;
    return *this;
  } 
};

(Это не может быть скомпилировано. Вы должны отделить реализации оператора присваивания из-за порядков объявления. Так что это просто, чтобы показать принцип)

1 голос
/ 19 сентября 2009

Документация библиотеки метапрограммирования Boost Template затрагивает такие вещи: http://www.boost.org/doc/libs/1_40_0/libs/mpl/doc/tutorial/dimensional-analysis.html.

0 голосов
/ 19 сентября 2009

Лакос касается этого (среди прочего).

По его мнению (и я с ним согласен) у вас не должно быть градусов и радианов, привязанных друг к другу. Если вам нужно преобразовать один в другой, то для переключения используйте третий класс / структуру.

Таким образом, у вас будет 3 класса Radians, Degrees и RadiansToDegrees, из которых вы можете использовать RadiansToDegrees в качестве простого функтора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...