我有一个用户必须通过的代码> 0号否则此代码将抛出。使用此arg的类型作为std :: size_t不起作用,因为负数给出大的正数。如果我使用签名类型或是否有其他方法来强制执行它,这是好的做法吗?
void f(std::size_t number)
{
//if -1 is passed I'm (for obvious reason) get large positive number
}
答案 0 :(得分:5)
我认为这个问题没有明确的正确答案。你可以看看Scott Meyers对这个问题的看法:
一个问题是无符号类型 倾向于降低你的能力 检测常见的编程错误。 另一个是他们经常增加 您的客户的可能性 类将使用这些类 不正确。
最后,要问的问题是:您是否需要无符号类型提供的额外可能值?
答案 1 :(得分:2)
很大程度上取决于您想象客户想要传递的论点类型。如果他们正在传递int,并且这显然足以容纳您将要使用的值范围,那么使用std :: size_t没有实际优势 - 它不会强制执行任何操作,以及问题的显示方式因为一个明显庞大的数字只会让人感到困惑。
但是 - 最好还是使用size_t,因为它有助于记录API的期望。
你显然不能对运行时生成的值进行“> 0”的编译时检查,但至少可以消除有意识的巨大数字的负输入ala
template <typename T>
void f(T t)
{
if (!(t > 0))
throw std::runtime_error("be positive!");
// do stuff with t, knowing it's not -1 shoehorned into size_t...
...
}
但是,如果你真的关心这个,你可以提供重载:
// call me please e.g. f(size_t(10));
void f(size_t);
// unimplemented (and private if possible)...
// "want to make sure you realise this is unsigned: call f(size_t) explicitly
void f(int32_t);
void f(int64_t);
...然后有一个编译时错误导致注释重新调用者显式提供size_t参数(必要时进行转换)。强制客户端提供size_t类型的arg是确保他们意识到这个问题的一种非常好的方法。
Rin也有一个好主意 - 在它可以工作的地方工作得很好(取决于有大于size_t的有符号int类型)。去看看吧....
编辑 - 上面模板构思的演示......
#include <iostream>
template <typename T>
void f(T t)
{
if (!(t > 0))
std::cout << "bad call f(" << (int)t << ")\n";
else
std::cout << "good f(" << (int)t << ")\n";
}
int main()
{
f((char)-1);
f((unsigned char)255);
}
答案 2 :(得分:2)
我遇到了同样的问题:Malfunctioning type-casting of string to unsigned int
因为在我的情况下,我得到了用户的输入,我的方法是将数据作为字符串读取并检查其内容。
template <class T>
T getNumberInput(std::string prompt, T min, T max) {
std::string input;
T value;
while (true) {
try {
std::cout << prompt;
std::cin.clear();
std::getline(std::cin, input);
std::stringstream sstream(input);
if (input.empty()) {
throw EmptyInput<std::string>(input);
} else if (input[0] == '-' && std::numeric_limits<T>::min() == 0) {
throw InvalidInput<std::string>(input);
} else if ((sstream >> value) && (value >= min) && (value <= max)) {
std::cout << std::endl;
return value;
} else {
throw InvalidInput<std::string>(input);
}
} catch (EmptyInput<std::string> & emptyInput) {
std::cout << "O campo não pode ser vazio!\n" << std::endl;
} catch (InvalidInput<std::string> & invalidInput){
std::cout << "Tipo de dados invãlido!\n" << std::endl;
}
}
}
答案 3 :(得分:1)
如果number
允许的值范围允许,则使用签名std::ptrdiff_t
(如Alexey所说)。
或者使用像SafeInt这样的库,让f
声明如下:void f( SafeInt< std::size_t > i );
,如果你用f( -1 );
之类的方式调用它,它会抛出。
答案 4 :(得分:0)
也许您应该将read-function包装到另一个函数中,该函数的目的是获取int
并验证它。
编辑:好的int
只是第一个想法,所以请阅读并解析string
答案 5 :(得分:0)
Fisrt解决方案
void f(std::ptrdiff_t number) {
if (number < 0) throw;
}
第二个解决方案
void f(std::size_t number) {
if (number > std::numeric_limits<std::size_t>::max()/2) throw;
}
答案 6 :(得分:0)
这是你无法做多少事情的情况之一。编译器通常在将签名转换为无符号数据类型时发出警告,因此您必须信任调用者注意该警告。
答案 7 :(得分:0)
您可以使用按位操作对此进行测试,例如:
void f(std::size_t number)
{
if(number & (0x1L << (sizeof(std::size_t) * 8 - 1)) != 0)
{
// high bit is set. either someone passed in a negative value,
// or a very large size that we'll assume is invalid.
// error case goes here
}
else
{
// valid value
}
}
此代码假定为8位字节。 =)
是的,大值会失败,但你可以记录它们是不被允许的,如果你真的需要防止它。
谁在使用此API?我建议他们修复代码,而不是使用这样的解决方案。 =)我认为“正常”的最佳做法是让调用者使用size_t,如果他们试图将签名值放入其中,他们的编译器会大声抱怨。
答案 8 :(得分:0)
我不得不考虑一下这个问题,这就是我要做的事。
如果您的函数负责在传递负数时抛出异常,那么您的函数的签名应接受签名整数。那是因为如果你接受一个无符号数字,你将永远无法明确告诉一个数字是否为负数而你将无法抛出异常。 IOW,你想投诉你的抛出异常。
您应该确定什么是可接受的输入范围,并使用足够大的有符号整数来完全包含该范围。