在科学计算中的数值量常带有单位,而不同单位的量容易相互混淆。下面的简单的C++代码实现了带单位的数值量计算。
// 用 g++-4.8.2编译,编译选项: -std=c++11
// hang_ke@sohu.com
template <class T, int u1, int u2 = 0, int u3 = 0, int u4 = 0, int u5 = 0>
class Unit {
template <class S, int v1, int v2, int v3, int v4, int v5> friend class Unit;
T value;
public:
typedef T value_type;
typedef Unit unit_type;
Unit() : value(0) {}
Unit(T v) : value(v) {}
Unit<T, u1*2, u2*2, u3*2, u4*2, u5*2> pow2() const {
return value * value;
}
Unit<T, u1*3, u2*3, u3*3, u4*3, u5*3> pow3() const {
return value * value * value;
}
Unit operator+(Unit unit) const { return value + unit.value; }
Unit operator-(Unit unit) const { return value - unit.value; }
Unit operator*(T v) const { return value * v; }
template<int v1, int v2, int v3, int v4, int v5>
Unit<T, u1+v1, u2+v2, u3+v3, u4+v4, u5+v5> operator*(Unit<T, v1, v2, v3, v4, v5> unit) const {
return value * unit.value;
}
template<int v1, int v2, int v3, int v4, int v5>
Unit<T, u1-v1, u2-v2, u3-v3, u4-v4, u5-v5> operator/(Unit<T, v1, v2, v3, v4, v5> unit) const {
return value / unit.value;
}
explicit operator T() const { return value; }
T inUnit(Unit unit) const { return value / unit.value; }
};
template<class T, int u1, int u2, int u3, int u4, int u5>
Unit<T, u1, u2, u3, u4, u5> operator*(T v, Unit<T, u1, u2, u3, u4, u5> unit) {
return unit * v;
}
typedef Unit<double, 1, 0, 0> Mass;
typedef Unit<double, 0, 1, 0> Length;
typedef Unit<double, 0, 0, 1> Time;
typedef Unit<double, 0, 2, 0> Area;
typedef Unit<double, 0, 3, 0> Volume;
const Mass kilogram(1), gram(0.001); // 值为1代表基本单位。
const Length meter(1), kilometer(1000);
const Time second(1), minute(60), hour(3600);
const auto second2 = second.pow2();
typedef decltype(meter/second) Speed; // 应可写为 decltype(Length/Time)?
typedef decltype(meter/second2) Acceleration;
typedef decltype(kilogram*meter/second2) Force;
const Force newton(1);
const Acceleration G = 9.8 * meter / second2;
#include <iostream>
int main() {
using namespace std;
Length length = 5. * kilometer;
Time time = 0.5 * hour;
Speed speed = length / time;
Force force = 50000. * gram * G;
cout << "speed(m/s) = " << (double)speed << endl
<< "speed(km/h) = " << speed.inUnit(kilometer/hour) << endl
<< "force(N) = " << (double)force << endl;
Speed speeds[] = { 1., 2., 3., 4., 5. }; // 原单位为km/h,需要转换。
cout << "speeds(m/s)[] = " << endl;
for (Speed& s : speeds) s = (double)s * kilometer / hour,
cout << " " << (double)s << endl;
return 0;
}
/* 程序输出
speed(m/s) = 2.77778
speed(km/h) = 10
force(N) = 490
speeds(m/s)[] =
0.277778
0.555556
0.833333
1.11111
1.38889
*/
说明:
1. 程序用C++11编写,但并非必要,用到的C++11特性可以绕开。
2. 选择基本单位时不必根据标准单位制,如可以用毫米而不是米,以提高数值精度。
3. 代码不支持一些功能,如非倍数的单位转换(如摄氏度-华氏度)。
4. 编译器应当能优化掉主要开销,如数量与单位常量的乘积在代码内联后应能使常量乘积合并为常数。
3. boost(www.boost.org)也提供了units库,但实现代码过于复杂不可读也不易学。议论:开源库(许多商业库也公开源代码)的代码应当清晰可读,这有助于用户检查代码、学习、改进(这是用户的权利,也是软件公司营造独特价值、占领用户市场的方法)。一些质量欠佳的开源库实际上在鼓励竞争及自行编写。
4. C++ STL的编译错误信息难以理解。这也鼓励商业编译器市场以及对C++语言的扩展改进。例如,下面的代码用非虚函数接口来约束template参数类型(参见 http://bbs.csdn.net/topics/390820400):
struct A {
void greet() = 0; // 非虚函数,C++语法扩展
A() = 0; // constructor
};
template <class T: A> // T不必显式继承A
class X {
void greet() {
T a;
a.greet();
}
};
顺便说一句,csdn应支持用email发表与获取blog文章(参见newsgroup),在网页上编辑不方便。