2010-11-11 98 views
3

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

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

这没有任何意义。 size_t是无符号的,所以总是一个正数,不能表示-1。您将不得不使用签名类型,或将size_t转换为签名类型,或测试其顶部(符号)位或其他内容。这可能有助于更多地了解这部分代码的更广泛的功能...... – 2010-11-11 09:37:17

+4

如果大小真的是一个很大的正数呢? ;) – swatkat 2010-11-11 09:38:21

+1

@保罗:这个问题有一定道理:如果你能叫'F(-1)','F'将收到相同的位表示一个'size_t'为-1,它会解释为一个巨大的无符号值:在这种情况下,它可能不是* 1,但肯定是*问题*。 – 2010-11-11 10:06:04

回答

2

很大程度上取决于您想象的客户尝试通过的参数类型。如果它们传递的是整数,并且这个值足够大以保存要使用的值的范围,那么使用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);              
} 
+0

非常感谢你的回答。我无法理解你的第一个例子。它是什么?它是一个叫做f的模板函数吗?如果是的话它的返回类型是什么?我想如何理解(f> 0)?如果你可以请你解释一下,我的朦胧的大脑,我会超过gratefull。 – 2010-11-11 10:37:36

+0

@There:对不起 - 遗漏了返回类型和参数类型!粗心。这个想法只是模板实例化知道调用者实际传入的类型,所以它可以提供类型(和有符号/无符号是类型的一部分)的感知验证。 'if(!(t> 0)throw'如果用int(-1)调用,它会提前退出,因为编译器仍然知道它是一个int,不会将它与合法的巨大size_t混淆。 – 2010-11-11 10:44:45

+0

对不起仍然不能掌握(f> 0)的概念,编译器如何知道传递给f的值是什么,你能向我解释一下吗? – 2010-11-11 10:49:51

0

或许你应该换读功能到另一个功能,用途将得到int和验证。

编辑:好的int只是第一个想法,让阅读和分析string

+0

这只适用于'sizeof(int)> sizeof(size_t)',这是不太可能的。对于防弹输入,您真的需要读取一个字符串并在将其转换为所需的整数类型之前对其进行验证。 – 2010-11-11 09:41:35

+0

@Paul R:如果你碰巧知道有一个大于size_t的有符号整数类型(远不能确定,特别是64位应用程序),那么你可以简单地使用它。这不是一个好的通用解决方案,但至少Rin的想法是可行的。 – 2010-11-11 10:10:23

+0

@Tony:是的,这将工作在*某些*病例,但它不是非常便携 – 2010-11-11 10:26:58

0

最前一页解决方案

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

这些都不允许size_t的全部有效输入。 – 2010-11-11 09:43:42

+1

而第二种解决方案显然是错误的:如果我想传递一个大于std :: numeric_limits /2的*有效数字? – Simone 2010-11-11 09:47:41

+0

@Simone。如果我想传递一个大于'std :: numeric_limits :: max()'的数字呢?使用大于'size_t'和'ptrdiff_t'的类型。对于32位编译器使用类似'__int64'的东西。对于64位编译器,请使用类似'__int128'的东西。 – 2010-11-11 09:54:46

4

我不认为有一个明确的正确答案对这个问题。你可以采取看看Scott Meyers的意见关于这个问题:

一个问题是无符号类型 趋于减少你的能力 检测常见的编程错误。 另一种情况是,他们经常增加 您的 类的客户将错误地使用类 的可能性。

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

+0

*什么*真的是主观的? – 2010-11-11 09:43:00

+0

@Paul:改为'我不认为这个问题有一个确定的正确答案' – icecrime 2010-11-11 09:45:09

+0

谢谢 - 现在更有意义。 – 2010-11-11 09:52:35

0

这是您无法真正做得太多的情况之一。编译器通常会在将签名转换为未签名的数据类型时发出警告,因此您必须相信调用者才能注意到该警告。

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

0

我不得不思考这个问题了一点,这是我会做什么。

如果你的函数有责任抛出异常,如果你传递一个负数,那么你的函数的签名应该接受一个签署的整数。这是因为如果你接受一个没有签名的号码,你永远无法明确地告诉号码是否定的,你将不能抛出异常。 IOW,你想投诉你的抛出异常的任务。

你应该建立什么是可以接受的输入范围和使用有符号整数大到足以完全包含范围。

+0

小心不要conf使用> 0与非负:-)。如果存在这样一个大于size_t的有符号整数的话,你的推理的声音就会存在(事实上,Rin早就回答了同样的想法)。 – 2010-11-11 10:52:00

+0

“没有任何...”没有澄清如果*范围0 .. std :: numeric_limits :: max()中的每个*值都可以作为输入。如果是这样的话,我会使用一个大的整数库给出函数的必要条件。无论如何,我真的不明白你的第一个陈述。 – Simone 2010-11-11 10:58:25

1

如果您的允许值范围为number,则允许使用签署的std::ptrdiff_t(如Alexey所述)。
或者使用像SafeInt这样的库,并且f声明如下:void f(SafeInt<std::size_t> i);如果你用f(-1);之类的东西来调用它,会抛出它。

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