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

时间:2017-10-12 02:07:08

标签: c++

我尝试使用一些用户定义的类型来表达单位,这样我就可以强类型化函数参数。例如,长度为毫米,速度为毫米/秒,加速度为每秒毫秒,等等。

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

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。

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

1 个答案:

答案 0 :(得分:1)

我认为你从错误的方向解决你的问题。您不希望转换任意单位,而是转换数量中的单位。

所以我会这样开始:

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来强制编译时评估。