使用unsigned int与检查是否为负

时间:2013-09-18 06:59:47

标签: c++

假设我有一个带整数的类,对于一个简单的C'tor应该总是正数:

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

让我们说有人不小心创建了一个值为-5的A对象。 当然,这是无效的,X将获得非常大的价值,因为MSB现在不代表数字符号。 问题是,现在我无法真正检查数字是否为负数或无效(可能是按位?)。

我应该避开unsigned而只使用普通int吗?通过这样做,如果给定值超出最大值或低于零,我可以抛出异常。

很想听听一些建议。

3 个答案:

答案 0 :(得分:36)

我想到了两种方法:

  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实例之前强制转换为无符号。这很安全但很笨拙。

  3. 请注意,禁止来自所有整数类型(例如enum s)的隐式转换,因此您需要做更多工作来制作这个傻瓜证明。

    以下是一个基于SFINAE的基本示例,如果它们涉及签名值,则接受之外的所有隐式转换: Live on Coliru

    #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);
    }
    

答案 1 :(得分:7)

  

让我们说有人不小心创建了一个值为-5

的A对象

尽管使程序足够强大以接受此类错误可能是一种好习惯,但错误的根本原因是一个草率的程序员没有启用足够的警告。

要了解问题的根源,您需要确保在启用所有警告的情况下编译代码,并且还可以考虑使用外部静态分析工具。

答案 2 :(得分:1)

我可能同意 DanielKO 关于pattern will pop up everywhere。请注意, sehe 基于SFINAE的解决方案不适用于uint64 - &gt; 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_;
};

live example