簡體   English   中英

C ++中的多個Integer類

[英]Multiple Integer-type classes in C++

我經常發現自己使用Integers來表示不同“空格”中的值。 例如...

int arrayIndex;
int usersAge;
int daysToChristmas;

理想情況下,我希望為“Index”,“Years”和“Days”這些類型中的每一種設置單獨的類,這應該可以防止我意外混淆它們。 從documnentation的角度來看,Typedef是一種幫助,但它不夠類型安全。

我已經嘗試過包裝器類,但最終卻有太多的樣板供我喜歡。 是否有一個簡單的基於模板的解決方案,或者可能是Boost中准備好的東西?

編輯:有幾個人在他們的答案中談到邊界檢查。 這可能是一個方便的副作用,但不是一個關鍵要求。 特別是,我不僅想要防止越界分配,而且要防止“不適當”類型之間的分配。

事實上,Boost確實有一個專門用於此類事情的庫! 查看Boost.Units庫

您可以使用的一個時髦的“hack”是用於創建包裝類型的模板非類型參數。 這不添加任何界限,但允許把它們看作是不同類型的只有一套樣板模板代碼。

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;

根據需要使用下限和上限或其他驗證擴展模板。 雖然遠遠不是很漂亮。

我記得用一個簡單的模板來解決類似的問題,你可以在其中指定允許的范圍,即

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

你明白了。 現在你可以從這樣的模板類型派生出來,比如

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

並且您不能再將用戶年齡傳遞給期望DayOfYear的函數,並且您不必使用有角度的括號。

你可以嘗試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.

這是一個通用的“StrongType”模板,我們用它來包裝不同的類型和上下文。 這個答案的唯一顯着區別是我們更喜歡使用標記類型 ,為每個專門的包裝類型提供有意義的名稱:

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;

還要注意,所有函數都是“內聯”的,目的是編譯器可以盡力生成與模板完全沒有使用時生成的完全相同的代碼!

除了Ryan Fox提到的Boost Units庫之外,還有Boost Constrained Value庫, 目前正在審核中

誰知道何時或是否會發布官方Boost版本,但無論如何你都可以嘗試一下

添加運算符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;

我希望這有幫助。

int arrayIndex;

這就是std::size_t的用途。

int usersAge;

人們不能有負面年齡,並且為年齡設定固定的上限是沒有用的/容易的。 所以在這里你應該使用unsigned int

int daysToChristmas;

聖誕節需要特別注意。 Christamas的天數范圍為0-366。 簡單的解決方案是在需要的地方編寫以下內容:

assert( 0 < daysToChristmas && daysToChristmas < 366 )

如果你覺得你要在太多地方復制那個assert ,那么David Allan Finch為這個案例提出了一個簡潔的解決方案。 雖然我偏愛使用斷言。

對於數組索引我會使用size_t,前提是我不需要負值,因為這就是它的用途。 當然,這通常是unsigned int,因此根本不會給你任何類型安全。 但是,任何確實為您提供類型安全性的東西(即停止為數組索引分配unsigned int)也會阻止您將size_t值返回到您的類型中。 無論如何,這可能是太多的類型安全。

您可以使用有限范圍的枚舉:

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

您可以將YearDay分配給int,但是如果沒有顯式強制轉換,則無法為YearDay分配int(或其他枚舉類型)。 枚舉中最小和最大命名值之間的任何值都是枚舉的有效值。 分配超出范圍[0,365]的值會導致未定義的行為。 或者可能是未指定或實現定義的結果,我不記得了。

年齡很棘手,因為它幾乎是有限的,但並不完全。 您可以在枚舉中使用969(Methuselah的年齡),或者使用其他人描述的顯式轉換包裝int的類。

查看關於此主題的這篇舊CUJ文章 IIRC該技術旨在使其與所有基本運營商合作

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM