我最近阅读了关于C ++ Source的系列文章,“暂停反映:五个五个列表”。在Part V中,Scott Meyers讨论了Barton和Nackman解决单位问题的方法。作为航空航天业的嵌入式软件工程师,这个特别的啊哈!片刻让我很兴奋。到目前为止,我还没有听说过这种方法(也不是这些作者)。
我已经做过研究,试图找到有关该解决方案的更多信息。我在这里看到了这个演示文稿:http://se.ethz.ch/~meyer/publications/OTHERS/scott_meyers/dimensions.pdf
我想我理解了我在这个解决方案上所阅读的所有内容。但我觉得有一块拼图丢失了。这个美丽,优雅的解决方案无处可扩展。具体来说,我对转换不仅仅是一个倍增因素感兴趣。例如,温度在开尔文,摄氏和华氏之间转换。我希望能够互换使用这些温度。
我的问题:
我错过了什么吗?是否参考我忽略的单位解决方案讨论讨论了规模?
如果没有,我该如何进一步解决这个问题?是否存在可与B& N方法结合使用以完成解决方案的现有模式?
我的目标是能够在不进行过多计算的情况下使用类似下面示例的代码。在距离的情况下,我希望能够声明一个定义为英里的对象,并以英里为单位执行所有相关计算,而不必经常来回转换为米。
示例:
typedef Units<double, miles> uMiles;
typedef Units<double, kilometers> uKilometers;
uMiles d1 (1.0);
uKilometers d2 (1.60934);
d1 += d2;
if (d1.val(miles) == 2.0) // PASS
if (d1.val(kilometers) == 3.21869) // PASS
注意: 我已经看到了BOOST UNITS解决问题的方法,我不喜欢它。对我来说这是非常难以理解的。我也不是,通常允许使用外部库,如boost。
备份数据:
如上所述的单位类:
template<class T, // Precision
int m, // Mass
int l, // Length
int t, // Time
int q, // Charge
int k, // Temperature
int i, // Luminous Intensity
int a> // Angle
class Units
{
public:
// ------------------------------------------------------
explicit
Units (T initVal = 0)
: val (initVal)
{
}
// --------------------------------------------------------------------
// Operator: Assignment from type T
Units<T, m, l, t, q, k, i, a>&
operator= (const T rhs)
{
val = rhs;
return *this;
}
// --------------------------------------------------------------------
// Operator: Type Converstion to T
operator T () const
{
return val;
}
// --------------------------------------------------------------------
// Operator: +=
Units<T, m, l, t, q, k, i, a>&
operator+= (const Units<T, m, l, t, q, k, i, a>& rhs)
{
val += rhs.val;
return *this;
}
// --------------------------------------------------------------------
Units<T, m, l, t, q, k, i, a>&
operator-= (const Units<T, m, l, t, q, k, i, a>& rhs)
{
val -= rhs.val;
return *this;
}
// --------------------------------------------------------------------
Units<T, m, l, t, q, k, i, a>&
operator*= (T rhs)
{
val *= rhs;
return *this;
}
// --------------------------------------------------------------------
Units<T, m, l, t, q, k, i, a>&
operator/= (T rhs)
{
val /= rhs;
return *this;
}
// --------------------------------------------------------------------
// Get Reference
T&
Val ()
{
return val;
}
// --------------------------------------------------------------------
// Get Value
const T&
Val () const
{
return val;
}
private:
T val;
};
// ----------------------------------------------------------------------------
// Operator: Addition
template<class T, int m, int d, int t, int q, int k, int i, int a>
const Units<T, m, d, t, q, k, i, a>
operator+ (const Units<T, m, d, t, q, k, i, a> & lhs,
const Units<T, m, d, t, q, k, i, a> & rhs)
{
Units<T, m, d, t, q, k, i, a> result (lhs);
return result += rhs;
}
// ----------------------------------------------------------------------------
// Operator: Subtraction
template<class T, int m, int d, int t, int q, int k, int i, int a>
const Units<T, m, d, t, q, k, i, a>
operator- (const Units<T, m, d, t, q, k, i, a> & lhs,
const Units<T, m, d, t, q, k, i, a> & rhs)
{
Units<T, m, d, t, q, k, i, a> result (lhs);
return result -= rhs;
}
// ----------------------------------------------------------------------------
// Operator: Multiplication
template<class T, int m, int d, int t, int q, int k, int i, int a>
const Units<T, m, d, t, q, k, i, a>
operator* (const Units<T, m, d, t, q, k, i, a> & lhs,
const Units<T, m, d, t, q, k, i, a> & rhs)
{
Units<T, m, d, t, q, k, i, a> result (lhs);
return result *= rhs;
}
// ----------------------------------------------------------------------------
// Operator: Division
template<class T, int m, int d, int t, int q, int k, int i, int a>
const Units<T, m, d, t, q, k, i, a>
operator/ (const Units<T, m, d, t, q, k, i, a> & lhs,
const Units<T, m, d, t, q, k, i, a> & rhs)
{
Units<T, m, d, t, q, k, i, a> result (lhs);
return result /= rhs;
}
// ----------------------------------------------------------------------------
// Operator: Multiplication (Creates New Type)
template<class T,
int m1, int d1, int t1, int q1, int k1, int i1, int a1,
int m2, int d2, int t2, int q2, int k2, int i2, int a2>
// Return Type
Units<T, m1 + m2, d1 + d2, t1 + t2, q1 + q2, k1 + k2, i1 + i2, a1 + a2>
operator* (const Units<T, m1, d1, t1, q1, k1, i1, a1>& lhs,
const Units<T, m2, d2, t2, q2, k2, i2, a2>& rhs)
{
// New Return type
typedef Units<T,
m1 + m2,
d1 + d2,
t1 + t2,
q1 + q2,
k1 + k2,
i1 + i2,
a1 + a2> ResultType;
return ResultType (lhs.Val() * rhs.Val());
}
// ----------------------------------------------------------------------------
// Operator: Division (Creates New Type)
template<class T,
int m1, int d1, int t1, int q1, int k1, int i1, int a1,
int m2, int d2, int t2, int q2, int k2, int i2, int a2>
// Return Type
Units<T, m1 - m2, d1 - d2, t1 - t2, q1 - q2, k1 - k2, i1 - i2, a1 - a2>
operator/ (const Units<T, m1, d1, t1, q1, k1, i1, a1>& lhs,
const Units<T, m2, d2, t2, q2, k2, i2, a2>& rhs)
{
// New Return type
typedef Units<
T,
m1 - m2,
d1 - d2,
t1 - t2,
q1 - q2,
k1 - k2,
i1 - i2,
a1 - a2> ResultType;
return ResultType (lhs.Val() / rhs.Val());
}
这个类允许我们编写如下代码:
// Base Types
typedef Units<double, 1,0,0,0,0,0,0> uMass;
typedef Units<double, 0,1,0,0,0,0,0> uLength;
typedef Units<double, 0,0,1,0,0,0,0> uTime;
typedef Units<double, 0,0,0,1,0,0,0> uCharge;
typedef Units<double, 0,0,0,0,1,0,0> uTemperature;
typedef Units<double, 0,0,0,0,0,1,0> uIntensity;
typedef Units<double, 0,0,0,0,0,0,1> uAngle;
// Derived Types
typedef Units<double, 0,2, 0,0,0,0,0> uArea;
typedef Units<double, 0,3, 0,0,0,0,0> uVolume;
typedef Units<double, 0,1,-1,0,0,0,0> uVelocity;
typedef Units<double, 0,1,-2,0,0,0,0> uAcceleration;
typedef Units<double, 1,1,-2,0,0,0,0> uForce;
uMass mass;
uTime time;
uForce force;
uLength length;
uVelocity velocity;
uAcceleration acceleration;
// This will compile
mass = 7.2;
acceleration = 3.5;
force = mass * acceleration;
// These will not compile ** Enforcing Dimensional Unit Correctness
force = 7.2 * acceleration;
force = mass;
force *= acceleration;
答案 0 :(得分:0)
根据我对您的代码和解释的理解,您似乎可以定义转换常量“Unit
s”,例如
Units<double,0,0,0,0,0,0,0> K2C(243.15);
Units<double,0,0,0,0,1,0,0> uCelsius;
Units<double,0,0,0,0,1,0,0> uKelvin;
uCelsius = uKelvin - K2C;
上面的代码与重载运算符一起使用,同时保持模板化单元的一致性。您必须为要使用的任何常量创建伪 - Unit
。
我能看到工作的另一种方式是写一个像
这样的函数typdef enum {
CELSIUS,
KELVIN,
FAHRENHEIT
} temp_t;
void Units::convertTemp(const temp_t from, const temp_t to) {
switch(from) {
case KELVIN:
val -= 243.15;
case CELSIUS:
if(to == FAHRENHEIT)
//conversion
else if(to == KELVIN)
val += 243.15;
break;
case FAHRENHEIT:
// convert to celsius
if(to == KELVIN)
//convert to Kelvin
}
}
答案 1 :(得分:0)
在物理学中,具有单位(如速度)的值乘以标量时保留其单位。这意味着说:
1.6 * 7 kilometers per hour = 11.2 kilometers per hour
不会更改单位。我们实际想要做的是将每小时公里数转换为每小时英里数,但我们只是将每小时公里数乘以一个因子。仅仅因为你得到的数字相当于每小时英里数并不意味着你实际上代表的是每小时里程数。
在模板定义中,您只允许每种基本单元。
到目前为止,您已区分不同类型的物理量,但不区分不同类型的单位系统。在上面的代码中,您的单位/类型为Speed
,而不是milesPerHour
,您仍然可以手动记住您正在使用的实际单位,并手动转换它与SI单位(不使用类型系统)。实际上,如果你有一个变量初始化为每小时英里数而另一个变量为每小时公里数,那么它们是相同类型还是不同类型?我认为它们应该是不同的类型。
要使所有单位(在不同的测量系统中)不同类型,我们可以为SI units system编写一个Units
模板,为{编写一个等效模板{3}}
template<class T, // Precision
int m, // Mass
int l, // Length
int t, // Time
int q, // Charge
int k, // Temperature
int i, // Luminous Intensity
int a> // Angle
class SIUnits
{
// ...
template<class T, // Precision
int m, // Mass
int l, // Length
int t, // Time
int q, // Charge
int k, // Temperature
int i, // Luminous Intensity
int a> // Angle
class ImperialUnits
{
// ...
现在我们需要弄清楚一种允许我们的类型系统在两个单位系统之间自动映射的好方法。
以下是一个可能的(但可怕)解决方案:
template<class T, // Precision
int m, // Mass
int l, // Length
int t, // Time
int q, // Charge
int k, // Temperature
int i, // Luminous Intensity
int a> // Angle
ImperialUnits<T, m, l, t, q, k, i, a>
convert(SIUnits<T, m, l, t, q, k, i, a> value)
{
T conversionFactor = 1.0;
for (int x = 0; x < m; ++x)
{
// This is some function that maps from one to the other.
conversionFactor *= siMassToImperialMassFactor;
conversionFactor += siMassToImperialMassOffset;
}
for (int x = m; x < 0; ++x)
{
// This is some function that maps from one to the other.
conversionFactor *= siMassToImperialMassFactor;
conversionFactor += siMassToImperialMassOffset;
}
// Do the same for other dimensions as well...
你看到Kelvin,Fahrenheit和Celsius的问题在于你没有改变测量的基础系统这一事实,这需要你制作一个新的类型,但只是手动记住小提琴假装在两个系统之间进行转换所需的因素。
从本质上讲,如果我们删除模板并使用纯类,我们会有类似的东西:
class Celsius;
class Kelvin;
class Fahrenheit
{
// ...
Fahrenheit(Celsius t); // Auto-convert from celsius
Fahrenheit(Kelvin t); // Auto-convert from Kelvin
// ...
这些是不同的类型,因此我们可以使用类型系统来定义它们之间的转换。
最后,我可以解释一下我实际提出的建议。在Units
模板中,您拥有每个物理维度的基础类型和标识符,其中一个解决方案是添加一个项目来表示所使用的测量系统,即
template<class T, // Precision
int SystemOfUnits, // Denotes the system of units used, SI or Imperial.
int m, // Mass
int l, // Length
int t, // Time
int q, // Charge
int k, // Temperature
int i, // Luminous Intensity
int a> // Angle
class Units
{
// etc.
但是,我不喜欢上述解决方案,因为SystemOfUnits
模板参数的值会编码有关模板的过多信息。在我的定义中,我可能会更明确地说明我的测量系统使用的单位,即
template<class T, // Precision
int m, // Mass
int l, // Length
int t, // Time
int q, // Charge
int k, // Temperature
int i, // Luminous Intensity
int a, // Angle
class M, // Mass unit type
class L, // Length unit type
class T, // Time unit type
class Q, // Charge unit type
class K, // Temperature unit type
class I, // Luminous Intensity unit type
class A> // Angle unit type
class Units
{
// etc.
这将迫使您使用一致的测量系统进行尺寸分析,但代价是模板参数加倍。