检测负数

时间:2010-11-11 09:33:54

标签: c++

我有一个用户必须通过的代码> 0号否则此代码将抛出。使用此arg的类型作为std :: size_t不起作用,因为负数给出大的正数。如果我使用签名类型或是否有其他方法来强制执行它,这是好的做法吗?

void f(std::size_t number)
{
//if -1 is passed I'm (for obvious reason) get large positive number
}

9 个答案:

答案 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,你想投诉你的抛出异常。

您应该确定什么是可接受的输入范围,并使用足够大的有符号整数来完全包含该范围。