用户定义的类型 - 单位、转换

User defined types - units, conversions

本文关键字:单位 转换 类型 定义 用户      更新时间:2023-10-16

我尝试制作一些用户定义的类型来表示单位,以便我可以强键入函数参数。 例如,毫米表示长度

,毫米每秒表示速度,每秒毫米表示加速度,等等。

到目前为止,我已经这样做了:

template<typename T, typename mksUnit>
struct Value
{
    // data member
    T val;
    // constructor
    constexpr explicit Value(T d) : val(d) {}
    // operator overloads
    Value & operator +=(const Value &rhs) { val += rhs.val; return *this; }
    // other math and compare
    // operators, etc.
}
template<int meters, int kilo, int seconds>
struct mksUnit
{
    enum { m = meters, k = kilo, s = seconds };
};

有了这个,我可以做这样的事情:

using meters = Value<double, mksUnit<1, 0, 0>>;
using mm = Value<double, mksUnit<-3, 0, 0>>;
constexpr mm operator""_mm(long double v) { return mm(static_cast<double>(v)); }
using mmpersec = Value<double, mksUnit<-3, 0, 1>>;
constexpr mmpersec operator""_mms(long double v) { return mmpersec(static_cast<double>(v)); }
using mmpersec2 = Value<double, mksUnit<-3, 0, 2>>;
constexpr mmpersec2 operator""_mms2(long double v) { return mmpersec2(static_cast<double>(v)); }

创建我可以使用的单位,例如:

mm dist = 5.5_mm;
mm dist1 = dist + 10_mm;
mmpersec velocity = mmpersec(50.0);
mmpersec2 accel = 100.0_mms2;
void set_accel(mmpersec2 accel) { _accel = accel; }

等。

我需要在某些类型之间进行转换,我正在寻找一种好方法。 我唯一的想法是从类继承并将类型重载添加到派生类中。

struct millimeters: public mm
{
    operator(meters) { return mm.val / 1000; }
}

或类似的东西。

我想做这样的事情:

meters len = meters(5.0);
len += millimeters(25);

例如,这应该将 len 设置为 1.025。

我的问题是,在派生的不同用户定义类型之间添加转换的最佳方法是什么,如上所示。

我认为你从错误的方向解决问题。您不想变换任意单位,而是变换数量内的单位。

所以我会这样开始:

struct Quantity {
    double value;
};
// Base Quantites
class Length : public Quantity {};
class Duration : public Quantity {};
// Base units
Length Meter() {
    Length l;
    l.value = 1;
    return l;
}
Length Millimeter() {
    Length l;
    l.value = 0.001;
    return l;
}
Duration Seconds() {
    Duration t;
    t.value = 1;
    return t;
}

现在我们整理了数量,我们可以开始转换了:

// It is a good Idea to use SFINAE here to enable this function
// only for derived classes of Quantity
template <class Quantity>
double transform(Quantity&& value, Quantity&& ratio) {
    return value.value / ratio.value;
}

派生单位的部分是棘手的:

template <class Q1, class Q2>
struct MultipliedQuantiy : public Quantity {};
// I would use SFINAE here too because it is a bad idea to 
// define a multiplication for all possible types.
template <class Q1, class Q2>
auto operator*(Q1 q1, Q2 q2) {
    MultipliedQuantiy<Q1, Q2> q;
    q.value = q1.value * q2.value;
    return  q;
}

作为一个例子,我们可以使用它:

auto area = 1000.0*Millimeter()*(1000.0*Millimeter());
auto squareMeter = 1.0*Meter()*Meter();
double area_in_m2 = transform(area, squareMeter);

所以这是基本的想法,关于如何做到这一点。其余操作由您决定。你也可以在很多地方使用 constexpr 来强制编译时求值。