繁体   English   中英

检测负数

[英]Detecting negative number

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

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

我认为这个问题没有明确的正确答案。 你可以看看Scott Meyers关于这个主题的意见:

一个问题是无符号类型往往会降低检测常见编程错误的能力。 另一个原因是它们通常会增加类的客户端错误地使用类的可能性。

最后,要问的问题是:您是否需要无符号类型提供的额外可能值?

很大程度上取决于您想象客户想要传递的论点类型。 如果他们正在传递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的signed 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);                                                     
}

我遇到了同样的问题: 将字符串类型转换为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;
        }
    }
}

如果你的允许值范围number允许它使用签署std::ptrdiff_t (像阿列克谢说)。
或者使用像SafeInt这样的库, 并使 f声明如下: void f( SafeInt< std::size_t > i ); 如果你用f( -1 );类的东西调用它会抛出f( -1 );

也许你应该将read-function包装到另一个函数中,该函数的目的是获取int并验证它。

编辑:Ok int只是第一个想法,所以读取和解析string

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

这是你不能做太多事情的情况之一。 编译器通常在将签名转换为无符号数据类型时发出警告,因此您必须信任调用者注意该警告。

您可以使用按位运算来测试它,例如:

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,如果他们试图将签名值放入其中,他们的编译器会大声抱怨。

我不得不考虑一下这个问题,这就是我要做的。

如果您的函数负责在传递负数时抛出异常,那么函数的签名应该接受有符号整数。 那是因为如果你接受一个无符号数字,你将永远无法明确告诉一个数字是否为负数而你将无法抛出异常。 IOW,你想投诉你的抛出异常的任务。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM