C ++中的多个Integer类

时间:2008-12-09 10:34:56

标签: c++ class boost templates

我经常发现自己使用Integers来表示不同“空格”中的值。例如......

int arrayIndex;
int usersAge;
int daysToChristmas;

理想情况下,我希望为“Index”,“Years”和“Days”这些类型中的每一种设置单独的类,这样可以防止我意外混淆它们。从documnentation的角度来看,Typedef是一种帮助,但不够类型安全。

我已经尝试过包装类,但最终却有太多的样板供我喜欢。是否有一个简单的基于模板的解决方案,或者可能是Boost中准备好的东西?

编辑:有几个人在他们的答案中谈到边界检查。这可能是一个方便的副作用,但不是一个关键要求。特别是,我不仅想要防止越界分配,而且要防止“不适当”类型之间的分配。

10 个答案:

答案 0 :(得分:8)

实际上,Boost确实有一个专门用于此类事情的库!查看Boost.Units library

答案 1 :(得分:7)

您可以使用的一个时髦的“黑客”是用于创建包装类型的模板非类型参数。这不会添加任何边界,但 允许将它们视为只有一组样板模板代码的不同类型。即。

template<unsigned i>
class t_integer_wrapper
  {
  private:
    int m_value;
  public:
     // Constructors, accessors, operators, etc.
  };

typedef t_integer_wrapper<1> ArrayIndex;
typedef t_integer_wrapper<2> UsersAge;

根据需要使用下限和上限或其他验证扩展模板。虽然远远不够漂亮。

答案 2 :(得分:5)

我记得用简单的模板解决了类似的问题,你可以在其中指定允许的范围,即

Int<0, 365> daysToChristmas;
Int<0, 150> usersAge;
Int<0, 6> dayOfWeek;

你明白了。现在你可以从这样的模板类型派生出来,比如

class DayOfYear: public Int<0, 365> {}

并且您无法再将用户年龄传递给期望DayOfYear的函数,并且您不必使用带角度的括号。

答案 3 :(得分:5)

您可以尝试BOOST_STRONG_TYPEDEF。来自boost/strong_typedef.hpp

// macro used to implement a strong typedef.  strong typedef
// guarentees that two types are distinguised even though the
// share the same underlying implementation.  typedef does not create
// a new type.  BOOST_STRONG_TYPEDEF(T, D) creates a new type named D
// that operates as a type T.

答案 4 :(得分:4)

这是一个通用的“StrongType”模板,我们用它来包装不同的类型和上下文。这个answer的唯一显着区别是我们更喜欢使用标记类型,它为每个专门的包装类型提供了一个有意义的名称:

template <typename ValueType, class Tag> class StrongType {
public:
  inline StrongType() : m_value(){}
  inline explicit StrongType(ValueType const &val) : m_value(val) {}
  inline operator ValueType () const {return m_value; }
  inline StrongType & operator=(StrongType const &newVal) {
    m_value = newVal.m_value;
    return *this;
  }
private:
  //
  // data
  ValueType m_value;
};

使用模板如下:

class ArrayIndexTag;
typedef StringType<int, ArrayIndexTag> StrongArrayIndex;
StringArrayIndex arrayIndex;

另外请注意,所有函数都是'inline',意图是编译器可以尽力生成完全相同的代码,如果模板根本没有使用的话!

答案 5 :(得分:2)

除了Ryan Fox提到的Boost Units库之外,还有一个Boost Constrained Value库,currently under review

谁知道何时或是否会发布官方Boost版本,但您可能try it out anyway

答案 6 :(得分:1)

添加运算符int()将允许您使用需要普通int的对象。您还可以添加operator =()以将其设置在范围内。

class DayType 
  {
  public:
    static int const low = 1;
    static int const high = 365;
  };

template<class TYPE>
class Int
  {
  private:
    int m_value;
  public:
     operator int () { return m_value; }
     operator = ( int i ) { /* check and set*/ }
  };

  Int<DayType> day;
  int d = day;
  day = 23;

我希望这会有所帮助。

答案 7 :(得分:0)

int arrayIndex;

这是std::size_t的用途。

int usersAge;

人们不能有负面年龄,并且为年龄设定固定的上限是没有用的/容易的。所以在这里你应该使用unsigned int

int daysToChristmas;

圣诞节需要特别注意。 Christamas的天数范围为0-366。简单的解决方案是在需要的地方写下以下内容:

assert( 0 < daysToChristmas && daysToChristmas < 366 )

如果您觉得要在太多地方复制assert,那么David Allan Finch就此案例提出了一个简洁的解决方案。虽然我偏爱使用断言。

答案 8 :(得分:0)

对于数组索引我会使用size_t,前提是我不需要负值,因为这就是它的用途。当然,这通常是unsigned int,因此根本不会给你任何类型安全。但是,任何确实为您提供类型安全性的东西(即停止将无符号整数分配给数组索引)也会阻止您将size_t值返回到您的类型中。无论如何,这可能是太多的类型安全。

你可以使用enum作为有界范围:

enum YearDay {
    FirstJan = 0,
    LastDecInLeapYear = 365
};

您可以将YearDay指定给int,但是如果没有显式强制转换,则无法为YearDay指定int(或其他枚举类型)。枚举中最小和最大命名值之间的任何值都是枚举的有效值。分配超出范围[0,365]的值会导致未定义的行为。或者可能是未指定或实现定义的结果,我不记得了。

年龄很棘手,因为几乎有界,但并不完全。您可以在枚举中使用969(Methuselah的年龄),或者使用其他人描述的显式转换来包装int的类。

答案 9 :(得分:0)

在此主题上查看此old CUJ article。 IIRC该技术旨在使其与所有基本运营商合作