24

単純なC'torで常に正でなければならない整数を持つクラスがあるとします:

class A {
    unsigned int x;
    public:
    A(unsigned int X) : x(X) {}
};

そして、誰かが誤って A オブジェクトを値 で作成したとしましょう-5。もちろん、これは有効ではなくX、MSB が番号記号を表していないため、非常に大きな値が得られます。問題は、数値が負か無効かを実際に確認できないことです (おそらくビット単位ですか?)。

を避けてunsignedプレーンを使用するだけintですか?そうすることで、指定された値が最大値を超えている場合、またはゼロ未満の場合に例外をスローできます。

いくつかの提案を聞きたいです。

4

3 に答える 3

37

次の 2 つのアプローチが思い浮かびます。

  1. 「署名済み」タイプの明示的な変換を追加します。

    #include <cassert>
    
    class A {
        unsigned int x;
        public:
        A(unsigned int X) : x(X) {}
        explicit A(int X) : x(static_cast<unsigned>(X)) {
            assert(X>=0); // note X, not x!
        }
    };
    
    int main()
    {
        A ok(5);
        A bad(-5);
    }
    
  2. より適切なオーバーロードを削除して、暗黙的な変換を禁止します。

    A(int X) = delete;
    A(long X) = delete;
    A(char X) = delete;
    

    これにより、すべてのユーザーは、A インスタンスを構築するに unsigned にキャストする必要があります。安全ですが、不器用です。

これはすべての整数型 ( s など)からの暗黙的な変換を禁止するわけではないので、これをばかにするためにさらに多くのことを行う必要があることに注意してください。enum

以下は、符号付きの値が含まれる場合を除き、すべての暗黙的な変換を受け入れる基本的な SFINAE ベースの例です。

#include <type_traits>
#include <limits>

class A {
    unsigned int x;
    public:
    template<typename T, typename = typename std::enable_if<std::is_integral<T>::value, void>::type>
    A(T X) : x(X)
    {
        static_assert(!std::numeric_limits<T>::is_signed, "Signed types cannot be accepted");
    }
};

int main()
{
    A ok(5u);
    A bad(-5);
}
于 2013-09-18T07:04:05.813 に答える
1

についてはおそらくDanielKOpattern will pop up everywhereに同意します。また、SFINAEベースのソリューションは uint64 -> uint32 truncationでは機能しないことに注意してください。だから私の答え:

class A {
 public:
  using value_type = unsigned int;

  template <class T>
  explicit A(T x): x_(boost::numeric_cast<value_type>(x)) { // can be check in debug only
    static_assert(std::is_integral<T>::value, "T must be integral");
  }

 private:
  value_type x_;
};

そして実例

于 2013-09-18T08:58:19.220 に答える