我有一个想法是由我在boost MTL库教程中阅读的一些文档所激发的。
基本前提是我想使用模板为我提供编译时类型检查错误。
即,假设我有两个测量单位,Radians和Degrees。获得类型安全最明显的方法是定义2个类:
struct Radian
{
float rad;
}
struct Degree
{
float deg;
}
这一切都很好,除了我能做的事情
func()
{
Radian r;
Degree d;
r.rad = d.deg;
}
如果我可以将这样的赋值标记为编译时类型错误,那将是很好的。这让我考虑以下几点:
struct Degree {};
struct Radian {};
template <class Data, class Unit>
struct Quantity
{
Data val;
Quantity<Data,Unit>() : val() {}
explicit Quantity<Data,Unit>(Data v) : val(v) {}
};
typedef Quantity<float,Radian> Rad;
typedef Quantity<float,Degree> Deg;
现在,使用Rad和Deg类型的func()的等效代码会将该赋值标记为编译时错误(并且使用显式设置,甚至做一些像Rad r = 2.0这样简单的事情被认为是编译时错误)
我真正想要的是一个具有这个附加单位属性的浮点数,可用于在编译时捕获逻辑错误(即,在函数中使用度数期望弧度),但是对于所有意图和目的,这些都是浮动。
作为一般性问题,您对此方法有何看法?我有点担心这是实现我的目标的错误方法,但它有一个奇怪的吸引力。此外,是否有一个“特征”或像升级概念检查,我可以用来确定数据是“漂浮像”。最后,有什么方法可以继承默认的操作实现,例如“&lt;&lt;”所以我不必在Quantity类中手动实现所有这些?
答案 0 :(得分:6)
我相信你正在寻找像Boost.Units这样的东西。我认为这是一个开始的地方。
答案 1 :(得分:3)
普通类型系统已经这样做了:
Radian sin(Radian const& val) { /* Do Stuff */ }
Degree sin(Degree const& val) { /* Do Stuff */ }
由于度数和弧度是不同的类型,因此您无法将它们分配给彼此。
您要阻止的是不可预防的(有权访问内部的开发人员仍然可以访问内部,因此可以执行任何操作)。阻止这种情况的方法不是让他们访问内部。
让Val私有化,现在他们不会造成任何伤害。
答案 2 :(得分:1)
Boost Template Metaprogramming Library文档涉及到这类事情:http://www.boost.org/doc/libs/1_40_0/libs/mpl/doc/tutorial/dimensional-analysis.html。
答案 3 :(得分:1)
C ++这样做的方法是将'deg'和'rad'设为私有,这样编译器就可以很容易地防止你做的任务。
然后添加赋值运算符:
struct Radian
{
private:
float rad;
public:
Radian &operator=(const Radian &r)
{
rad = r.rad;
return *this;
}
Radian &operator=(const Degree &d)
{
rad = d.deg * DegToRadFactor;
return *this;
}
};
struct Degree
{
private:
float deg;
public:
Degree &operator=(const Degree &d)
{
deg = r.deg;
return *this;
}
Degree &operator=(const Radian &r)
{
deg = r.rad / DegToRadFactor;
return *this;
}
};
(这不可编译。由于声明命令,你必须将赋值运算符实现分开。所以这只是为了显示原理)
答案 4 :(得分:0)
Lakos触及这个(除其他事项外)。
在他看来(我恰好同意他)你不应该有相互关联的学位和弧度。如果你需要从一个转换为另一个,那么有第三个类/结构来执行切换。
因此,您将拥有3个类Radians
,Degrees
和RadiansToDegrees
,其中您可以使用RadiansToDegrees
作为简单的仿函数。