C ++ 0x с использованием <ratio>для более безопасного типа длины - PullRequest
1 голос
/ 31 мая 2011

При чтении <ratio> и <chrono> я попытался представить тип Length, который защищает от случайных ошибок преобразования.

Вот что я получил:

#include <iostream>
#include <ratio>
using namespace std;

template<typename Scale>
struct Length  {
  long long val_;
  Length(long long val) : val_{val} {}
  Length() = default;
  Length(const Length&) = default;
  Length& operator=(const Length&) = default;
  // conversion
  template<typename Scale2>
  Length(const Length<Scale2> &other)
    : val_{ other.val_*(Scale2::num*Scale::den)/(Scale2::den*Scale::num) }
  { }
  // access
  long long value() const { return val_; }
}; 
typedef Length<ratio<1>> m;
typedef Length<kilo> km;
typedef Length<milli> mm;
typedef Length<ratio<1000,1094>> yard;

Для использования в таком виде

int main() {
  km len_km = 300;
  mm len_mm = len_km;
  cout << " millimeter:" << len_mm.value() << endl;
  cout << " m:" << m{len_km}.value() << endl;
  cout << " yd:" << yard{len_km}.value() << endl;
}

И теперь я могу добавить все операции + и *, чтобы по-настоящему удобно ...: -)

Интересно:

  • Есть ли более легкий доступ к арифметическим средствам , которые duration и time_point определяют в любом случае <chrono>? Могу ли я использовать те, которые уменьшают усилия для Length?
  • Постоянная времени компиляции (Scale2::num*Scale::den)/(Scale2::den*Scale::num) кажется опасной в конструкторе преобразования (дробь / недополнение?), Но я не могу найти лучший способ метапрограммирования, какие-нибудь подсказки здесь?

Ответы [ 2 ]

2 голосов
/ 31 мая 2011
  • Есть ли более легкий доступ к арифметические средства что duration и time_point определяют в <chrono> в любом случае? Могу ли я использовать эти уменьшить усилие на Length?

Для арифметики "смешанного режима" и сравнений вы можете воспользоваться common_type<T1, T2>::type для определения типов возвращаемых значений. duration специализирует common_type как наибольший общий делитель Period1 и Period2, где Period1 и Period2 являются двумя ratio, участвующими в операции арифметики или сравнения. Вы можете использовать это как:

template <typename Scale1, typename Scale2>
  typename std::common_type<Length<Scale1>, Length<Scale2>>::type
  operator+(Length<Scale1> x, Length<Scale2> y);

К сожалению, вам придется заново изобрести, как получить наибольший общий делитель двух ratio с во время компиляции. Начните с мета-функций gcd ​​и lcm времени компиляции для unsigned long long.

Хм ... или вы могли бы основать свою специализацию common_type на уже сделанной для duration. Вы можете переосмыслить результирующий duration period как масштабный коэффициент для вашего Length. Я не прототипировал это, просто идея.

  • Постоянная времени компиляции (Scale2::num*Scale::den)/(Scale2::den*Scale::num) кажется опасным в преобразовании конструктор (дробь / недополнение?), но я не могу понять лучше способ метапрограммирования, есть здесь какие-то подсказки?

Согласовано. duration обрабатывает это с помощью:

template <class Rep2, class Period2>
  constexpr duration(const duration<Rep2, Period2>& d);

Замечания: Этот конструктор не должен участвовать в разрешении перегрузки если treat_as_floating_point<rep>::value не верно или оба ratio_divide<Period2, period>::den есть 1 и treat_as_floating_point<Rep2>::value ложно. [ Примечание: Это требование предотвращает неявная ошибка усечения при преобразование между интегральными типы продолжительности. Такая конструкция может легко привести к путанице значение продолжительности. - конечная нота ]

т.е. вам нужно enable_if ваш Length конструктор преобразования так, чтобы он существовал, только если преобразование является точным (если вы хотите основывать свою длину на целочисленных типах). Чтобы преобразование было точным, коэффициент преобразования (Scale2::num*Scale::den)/(Scale2::den*Scale::num) должен быть вычисляемым без деления (кроме деления на 1). Вы можете использовать ratio_divide, чтобы сделать это деление для вас, и тогда полученный знаменатель должен быть 1 (для точного преобразования).

enable_if<ratio_divide<Scale2, Scale1>::type::den == 1, ...>

Это отличный проект для изучения ratio! Повеселись! : -)

1 голос
/ 31 мая 2011

Вы используете целочисленный тип для представления физической величины.Это не то, что люди обычно хотят.Если вы настаиваете на целочисленном типе, по крайней мере, делайте умножение и деление в правильном порядке, то есть сначала все умножения, а затем деление (сравните 100 * (255/256) и (100 * 255) / 256).

Обратите внимание на то, что при умножении длины на длину вы получаете площадь, а не длину.Существуют реальные библиотеки, которые учитывают это, см., Например, siunits .

...