24

假设我有一个带有整数的类,它应该使用简单的 C'tor 始终为正:

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

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

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

很想听听一些建议。

4

3 回答 3

37

想到了两种方法:

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

请注意,这并不禁止来自所有整数类型(例如s)的隐式转换,enum因此您需要做更多的工作来证明这一点。

这是一个基于 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);
}
于 2013-09-18T07:04:05.813 回答
7

假设有人不小心创建了一个值为 -5 的 A 对象

虽然让您的程序足够健壮以接受此类错误可能是一种好习惯,但该错误的根本原因是一个草率的程序员没有启用足够的警告。

要找出问题的根源,您需要确保在编译代码时启用所有警告,也许还可以考虑使用外部静态分析工具。

于 2013-09-18T09:22:44.883 回答
1

我可能同意DanielKO关于pattern 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 回答