简体   繁体   English

检测负数

[英]Detecting negative number

I have a code in which user has to pass > 0 number otherwise this code will throw. 我有一个代码,其中用户必须传递> 0数字否则此代码将抛出。 Using type of this arg as std::size_t doesn't work for the reason that negative numbers give large positive numbers. 使用此arg的类型作为std :: size_t不起作用,因为负数给出大的正数。 Is it good practice if I use signed type or are there other ways to enforce it? 如果我使用签名类型或是否有其他方法来强制执行它,这是好的做法吗?

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

I don't think there's a definitive correct answer to this question. 我认为这个问题没有明确的正确答案。 You can take an look to Scott Meyers 's opinion on the subject : 你可以看看Scott Meyers关于这个主题的意见:

One problem is that unsigned types tend to decrease your ability to detect common programming errors. 一个问题是无符号类型往往会降低检测常见编程错误的能力。 Another is that they often increase the likelihood that clients of your classes will use the classes incorrectly. 另一个原因是它们通常会增加类的客户端错误地使用类的可能性。

In the end, the question to ask is really : do you need the extra possible values provided by the unsigned type ? 最后,要问的问题是:您是否需要无符号类型提供的额外可能值?

A lot depends on what type of argument you imagine your clients trying to pass. 很大程度上取决于您想象客户想要传递的论点类型。 If they're passing int, and that's clearly big enough to hold the range of values you're going to use, then there's no practical advantage to using std::size_t - it won't enforce anything, and the way the issue manifests as an apparently huge number is simply more confusing. 如果他们正在传递int,并且这显然足以容纳您将要使用的值范围,那么使用std :: size_t没有实际优势 - 它不会强制执行任何操作,以及问题的显示方式因为一个显然庞大的数字更令人困惑。

BUT - it is good to use size_t anyway as it helps document the expectations of the API. 但是 - 最好使用size_t,因为它有助于记录API的期望。

You clearly can't do a compile-time check for "> 0" against a run-time generated value, but can at least disambiguate negative inputs from intentional huge numbers ala 你显然不能对运行时生成的值进行“> 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...
    ...
}

But, if you are really concerned about this, you could provide overloads: 但是,如果你真的关心这个,你可以提供重载:

// 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);

...then there's a compile-time error leading to the comments re caller explicitly providing a size_t argument (casting if necessary). ...然后有一个编译时错误导致注释重新调用者显式提供size_t参数(必要时进行转换)。 Forcing the client to provide an arg of size_t type is a pretty good way to make sure they're conscious of the issue. 强制客户端提供size_t类型的arg是一种很好的方法,可以确保他们意识到这个问题。

Rin's got a good idea too - would work really well where it works at all (depends on there being an signed int type larger than size_t). Rin也有一个好主意 - 它可以在它工作的地方工作得很好(取决于有一个大于size_t的signed int类型)。 Go check it out.... 去看看吧....

EDIT - demonstration of template idea above... 编辑 - 以上模板构思的演示......

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

I had the same problems you're having: Malfunctioning type-casting of string to unsigned int 我遇到了同样的问题: 将字符串类型转换为unsigned int

Since, in my case, I'm getting the input from the user, my approach was to read the data as a string and check its contents. 因为在我的情况下,我得到了用户的输入,我的方法是将数据作为字符串读取并检查其内容。

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

If your allowed value range for number allows it use the signed std::ptrdiff_t (like Alexey said). 如果你的允许值范围number允许它使用签署std::ptrdiff_t (像阿列克谢说)。
Or use a library like SafeInt and have f declared something like this: void f( SafeInt< std::size_t > i ); 或者使用像SafeInt这样的库, 并使 f声明如下: void f( SafeInt< std::size_t > i ); which throws if you'll call it with something like f( -1 ); 如果你用f( -1 );类的东西调用它会抛出f( -1 ); .

Maybe you should wrap read-function to another function which purpose will be get int and validate it. 也许你应该将read-function包装到另一个函数中,该函数的目的是获取int并验证它。

EDIT: Ok int was just first idea, so read and parse string 编辑:Ok int只是第一个想法,所以读取和解析string

Fisrt solution Fisrt解决方案

void f(std::ptrdiff_t number) {
   if (number < 0) throw;
}

Second solution 二解决方案

void f(std::size_t number) {
   if (number > std::numeric_limits<std::size_t>::max()/2) throw;
}

This is one of the situations where you cannot really do much. 这是你不能做太多事情的情况之一。 The compiler usually gives out a warning when converting signed to unsigned data-types, so you will have to trust the caller to heed that warning. 编译器通常在将签名转换为无符号数据类型时发出警告,因此您必须信任调用者注意该警告。

You could test this using bitwise operation, such as the following: 您可以使用按位运算来测试它,例如:

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

This code assumes 8-bit bytes. 此代码假定为8位字节。 =) =)

Yes, large values will fail, but you could document that they are not allowed, if you really need to protect against this. 是的,大值会失败,但你可以记录它们是不被允许的,如果你真的需要防止它。

Who is using this API? 谁在使用此API? Rather than using a solution like this, I would recommend that they fix their code. 我建议他们修复代码,而不是使用这样的解决方案。 =) I would think that the "normal" best practice would be for callers to use size_t, and have their compilers complain loudly if they try to put signed values into them. =)我认为“正常”的最佳做法是让调用者使用size_t,如果他们试图将签名值放入其中,他们的编译器会大声抱怨。

I had to think about this question a little, and this is what I would do. 我不得不考虑一下这个问题,这就是我要做的。

If your function has the responsability to throw an exception if you pass a negative number, then your function's signature should accept a signed integer number. 如果您的函数负责在传递负数时抛出异常,那么函数的签名应该接受有符号整数。 That's because if you accept an unsigned number, you won't ever be able to tell unambiguosly if a number is negative and you won't be able to throw an exception. 那是因为如果你接受一个无符号数字,你将永远无法明确告诉一个数字是否为负数而你将无法抛出异常。 IOW, you want complain to your assignment of throwing exception. IOW,你想投诉你的抛出异常的任务。

You should establish what is an acceptable input range and use a signed integer large enough to fully contain that range. 您应该确定什么是可接受的输入范围,并使用足够大的有符号整数来完全包含该范围。

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

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