2010-11-11 97 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; 
     } 
    } 
}