C ++模板滥用问题 - 使用其他类型信息扩充浮动

时间:2009-09-19 04:02:33

标签: c++ templates

我有一个想法是由我在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类中手动实现所有这些?

5 个答案:

答案 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个类RadiansDegreesRadiansToDegrees,其中您可以使用RadiansToDegrees作为简单的仿函数。

相关问题