假设我有一个带整数的类,对于一个简单的C'tor应该总是正数:
class A {
unsigned int x;
public:
A(unsigned int X) : x(X) {}
};
让我们说有人不小心创建了一个值为-5
的A对象。
当然,这是无效的,X
将获得非常大的价值,因为MSB现在不代表数字符号。
问题是,现在我无法真正检查数字是否为负数或无效(可能是按位?)。
我应该避开unsigned
而只使用普通int
吗?通过这样做,如果给定值超出最大值或低于零,我可以抛出异常。
很想听听一些建议。
答案 0 :(得分:36)
我想到了两种方法:
为“已签名”类型添加显式转换。
#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);
}
通过删除更好的重载来禁止隐式转换:
A(int X) = delete;
A(long X) = delete;
A(char X) = delete;
这将要求所有用户在构建A实例之前强制转换为无符号。这很安全但很笨拙。
请注意,不禁止来自所有整数类型(例如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_;
};