4

整数を使用して、さまざまな「空間」の値を表すことがよくあります。例えば...

int arrayIndex;
int usersAge;
int daysToChristmas;

理想的には、これらのタイプ「Index」、「Years」、および「Days」のそれぞれに個別のクラスを用意したいと考えています。これにより、誤ってそれらを混同するのを防ぐことができます。Typedef は文書化の観点からは役立ちますが、タイプセーフとは言えません。

ラッパー クラスを試してみましたが、定型文が多すぎて好みに合わなくなりました。簡単なテンプレート ベースのソリューションはありますか、それとも Boost ですぐに使用できるものはありますか?

編集:何人かの人々が、回答で境界チェックについて話しました。これは便利な副作用かもしれませんが、重要な要件ではありません。特に、範囲外の割り当てだけでなく、「不適切な」タイプ間の割り当ても防止したいと考えています。

4

10 に答える 10

8

実際、Boost には、このタイプのもの専用のライブラリがあります。Boost.Units ライブラリを確認してください。

于 2008-12-09T11:30:57.063 に答える
7

使用できるファンキーな「ハック」の 1 つは、テンプレートの非型パラメーターを使用してラッパー型を作成することです。これは境界を追加しませんが、ボイラープレート テンプレート コードの 1 つのセットだけでそれらを異なる型として扱うことができます。いえ

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;

必要に応じて、下限と上限またはその他の検証を使用してテンプレートを拡張します。しかし、ロングショットではきれいではありません。

于 2008-12-09T10:44:40.073 に答える
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.
于 2008-12-09T12:32:36.877 に答える
5

許可された範囲を指定する単純なテンプレートで同様の問題を解決したことを覚えています。

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

あなたは要点を理解します。このようなテンプレート タイプから派生させることができます。

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

また、DayOfYear を期待する関数にユーザーの年齢を渡すことができなくなり、角かっこを使用する必要がなくなります。

于 2008-12-09T10:46:21.817 に答える
4

これは、さまざまなタイプとコンテキストをラップするために使用する汎用の「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;

また、すべての関数が「インライン」であることにも注意してください。これは、テンプレートがまったく使用されていない場合に生成されるコードとまったく同じコードをコンパイラが生成するために最善を尽くすためです。

于 2008-12-09T11:13:07.020 に答える
2

RyanFoxが言及したBoostUnitsライブラリに加えて、現在レビュー中のBoostConstrainedValueライブラリもあります。

Boostの公式リリースがいつリリースされるか、いつリリースされるかは誰にもわかりませんが、とにかく試してみることができます。

于 2008-12-09T11:48:45.467 に答える
1

演算子 int () を追加すると、通常の int が必要なオブジェクトを使用できるようになります。演算子 = () を追加して範囲内に設定することもできます。

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;

これが役立つことを願っています。

于 2008-12-09T10:54:41.613 に答える
0

このテーマに関するこの古い CUJ 記事をチェックしてください。IIRCのテクニックは、すべての基本的な演算子で機能させる方法を説明しています

于 2009-03-17T05:42:47.277 に答える
0
int arrayIndex;

これがstd::size_t目的です。

int usersAge;

人々は負の年齢を持つことはできず、年齢に固定の上限を設定することは役に立ちません/簡単ではありません。したがって、ここでは を使用する必要がありますunsigned int

int daysToChristmas;

クリスマスまでの日々は特に注意が必要です。クリスマスまでの日数は、0 ~ 366 の範囲で指定できます。簡単な解決策は、必要に応じて次のように記述することです。

assert( 0 < daysToChristmas && daysToChristmas < 366 )

あまりにも多くの場所でこれを繰り返すつもりならassert、David Allan Finch がこの場合の優れた解決策を提案します。私はアサートを使用することに部分的ですが。

于 2008-12-09T11:12:03.200 に答える
0

配列インデックスの場合、負の値が必要ない場合は size_t を使用します。それが目的だからです。もちろん、それは unsigned int であることが多いため、型の安全性はまったく得られません。ただし、型の安全性を提供したもの (つまり、unsigned int を配列インデックスに割り当てるのを停止したもの) は、型に size_t 値を返すことも停止します。とにかく、それは型の安全性が高すぎるかもしれません。

おそらく、境界のある範囲に列挙型を使用できます。

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

YearDay を int に割り当てることはできますが、明示的なキャストなしで int (または別の列挙型) を YearDay に割り当てることはできません。列挙型の最小の名前付き値と最大の名前付き値の間の値は、列挙型の有効な値です。[0,365] の範囲外の値を割り当てると、未定義の動作が発生します。または、不特定または実装定義の結果である可能性がありますが、思い出せません。

年齢はほとんど制限されているため注意が必要ですが、完全ではありません。列挙型で969(メトセラの時代)を使用するか、他の人が説明したように明示的な変換でintをラップするクラスを使用できます。

于 2008-12-09T11:38:27.157 に答える