मैं "हस्ताक्षरित / अहस्ताक्षरित बेमेल" चेतावनी (C4018) से कैसे निपटूं?


80

मैं C ++ में उच्च प्रदर्शन और कम मेमोरी ओवरहेड को ध्यान में रखते हुए बहुत सारे गणना कोड के साथ काम करता हूं। यह एसटीएल कंटेनरों (ज्यादातर vector) का उपयोग करता है , और लगभग हर एक फ़ंक्शन में उस कंटेनरों पर पुनरावृत्त करता है।

पुनरावृति कोड इस तरह दिखता है:

for (int i = 0; i < things.size(); ++i)
{
    // ...
}

लेकिन यह हस्ताक्षरित / अहस्ताक्षरित बेमेल चेतावनी (विजुअल स्टूडियो में C4018) का उत्पादन करता है ।

intकुछ unsignedप्रकार के साथ बदलना एक समस्या है क्योंकि हम अक्सर ओपनएमपी प्रैग्मैस का उपयोग करते हैं, और इसके लिए काउंटर की आवश्यकता होती है int

मैं (सैकड़ों) चेतावनियों को दबाने वाला हूं, लेकिन मुझे डर है कि मैंने समस्या के कुछ सुरुचिपूर्ण समाधान को याद किया है।

पुनरावृत्तियों पर । मुझे लगता है कि उपयुक्त स्थानों पर लागू होने पर पुनरावृत्तियाँ महान हैं। मैं जिस कोड के साथ काम कर रहा हूं, वह कभी भी रैंडम-एक्सेस कंटेनरों को listया कुछ में नहीं बदलेगा (इसलिए int iपहले से ही कंटेनर एग्नॉस्टिक है), और हमेशा वर्तमान सूचकांक की आवश्यकता होगी । और सभी अतिरिक्त कोड आपको टाइप करने की आवश्यकता होती है (इट्रेटर ही और इंडेक्स) बस मामलों को उलझाते हैं और अंतर्निहित कोड की सादगी को बाधित करते हैं।


1
क्या आप एक उदाहरण पोस्ट कर सकते हैं जहां OpenMP प्रैगमा आपको एक अहस्ताक्षरित प्रकार का उपयोग करने से रोकता है? के अनुसार इस यह, किसी भी Intergal प्रकार के लिए काम करना चाहिए न सिर्फ int
बिली ओनेल

4
मेरा मानना ​​है कि यह सवाल स्टैकओवरफ्लो के लिए बेहतर है।
bcsanches

1
intऔर std::vector<T>::size_typeआकार में भिन्न होने के साथ-साथ हस्ताक्षर में भी भिन्न हो सकते हैं। उदाहरण के लिए, एलएलपी 64 प्रणाली (जैसे 64-बिट विंडोज) पर, sizeof(int) == 4लेकिन sizeof(std::vector<T>::size_type) == 8
एड्रियन मैक्कार्थी


के संभावित डुप्लिकेट stackoverflow.com/questions/8188401/...
CinCout

जवाबों:


60

यह आपके things.size()प्रकार में है। यह नहीं है int, लेकिन size_t(यह C ++ में मौजूद है, C में मौजूद नहीं है) जो unsigned intx86-32 के लिए कुछ "सामान्य" अहस्ताक्षरित प्रकार के बराबर है।

ऑपरेटर "कम" (<) अलग संकेत के दो ऑपरेंड पर लागू नहीं किया जा सकता है। बस ऐसा कोई ऑपकोड नहीं है, और मानक निर्दिष्ट नहीं करता है, चाहे संकलक अंतर्निहित संकेत रूपांतरण कर सकता है। तो यह सिर्फ हस्ताक्षर किए गए नंबर को अहस्ताक्षरित मानता है और उस चेतावनी का उत्सर्जन करता है।

इसे लिखना सही होगा

for (size_t i = 0; i < things.size(); ++i) { /**/ }

या इससे भी तेज

for (size_t i = 0, ilen = things.size(); i < ilen; ++i) { /**/ }

17
-1 नहीं, यह नहीं है size_t। इसकी है std::vector< THING >::size_type
राडवल्ड

8
@Raedwald: जब आप तकनीकी रूप से सही होते हैं, तो यह कल्पना करना कठिन है कि मानकों का अनुपालन कैसे लागू हो सकता है std::size_tऔर इसके लिए विभिन्न अंतर्निहित प्रकारों के साथ समाप्त हो सकता है std::vector<T>::size_type
एड्रियन मैक्कार्थी

4
++ मुझे बेहतर क्यों माना जा रहा है? छोरों के लिए "नहीं" अंतर नहीं है?
शोएब

2
@ ShoaibHaider, यह उन प्राथमिकताओं के लिए बिल्कुल भी मायने नहीं रखता है जहाँ रिटर्न वैल्यू का उपयोग नहीं किया जा रहा है। हालाँकि, कस्टम प्रकारों के लिए (जहाँ ऑपरेटर को ओवरलोड किया जाता है), पोस्ट इंक्रीमेंट लगभग हमेशा कम कुशल होता है (क्योंकि इसे बढ़ने से पहले ऑब्जेक्ट की कॉपी बनाना पड़ता है)। कंपाइलर कस्टम प्रकारों के लिए (आवश्यक) अनुकूलन नहीं कर सकते हैं। तो एकमात्र लाभ संगति (प्राइमेटिस बनाम कस्टम प्रकार) का है।
कैट 21

2
@ मेग्निथ: हाँ, आप सही कह रहे हैं। मेरा बयान केवल डिफ़ॉल्ट आवंटनकर्ता के लिए है। एक कस्टम एलोकेटर std :: size_t के अलावा कुछ का उपयोग कर सकता है, लेकिन मेरा मानना ​​है कि यह अभी भी एक अहस्ताक्षरित अभिन्न प्रकार होगा, और यह शायद std की तुलना में एक बड़ी रेंज का प्रतिनिधित्व नहीं कर सकता है: size_t, इसलिए यह अभी भी std का उपयोग करने के लिए सुरक्षित है :: size_t लूप इंडेक्स के प्रकार के रूप में।
एड्रियन मैकार्थी

13

आदर्श रूप में, मैं इसके बजाय एक निर्माण का उपयोग करूंगा:

for (std::vector<your_type>::const_iterator i = things.begin(); i != things.end(); ++i)
{
  // if you ever need the distance, you may call std::distance
  // it won't cause any overhead because the compiler will likely optimize the call
  size_t distance = std::distance(things.begin(), i);
}

इसका एक बड़ा फायदा है कि आपका कोड अचानक कंटेनर अज्ञेय बन जाता है।

और आपकी समस्या के बारे में, यदि आपके द्वारा उपयोग की जाने वाली कुछ लाइब्रेरी के लिए आपको उपयोग करने की आवश्यकता होती है intजहां unsigned intबेहतर फिट होगा, तो उनका एपीआई गड़बड़ है। वैसे भी, यदि आप सुनिश्चित हैं कि वे intहमेशा सकारात्मक हैं, तो आप बस कर सकते हैं:

int int_distance = static_cast<int>(distance);

जो संकलक को आपके इरादे को स्पष्ट रूप से निर्दिष्ट करेगा: यह आपको चेतावनी के साथ बग नहीं देगा।


1
मैं हमेशा दूरी की जरूरत है। शायद static_cast<int>(things.size())समाधान हो सकता है, अगर कोई अन्य नहीं है।
एंड्रयू टी

@ भारत: यदि आप चेतावनी को दबाने का फैसला करते हैं, तो सबसे अच्छा तरीका यह होगा कि आप #pragma warning(push) #pragma warning(disable: 4018) /* ... function */ #pragma warning(pop)किसी अनावश्यक कलाकार का उपयोग करने के बजाय एक कंपाइलर विशिष्ट प्रज्ञा (MSVC पर ) का उपयोग करेंगे। (कास्ट्स वैध त्रुटियों को छिपाते हैं, m'kay?))
बिली

सच नहीं। कास्ट! = रूपांतरण। चेतावनी मानक द्वारा अनुमत अंतर्निहित रूपांतरण के बारे में चेतावनी दे रही है जो असुरक्षित हो सकता है। एक कलाकार, हालांकि, पार्टी के लिए स्पष्ट रूपांतरणों को आमंत्रित करता है जो कि चेतावनी के बारे में मूल रूप से बात की तुलना में अधिक असुरक्षित हो सकता है। कड़ाई से बोलते हुए, कम से कम x86 पर, कोई रूपांतरण बिल्कुल नहीं होने जा रहा है - प्रोसेसर को परवाह नहीं है कि क्या आप मेमोरी के एक विशिष्ट हिस्से को हस्ताक्षरित या अहस्ताक्षरित मान रहे हैं, इसलिए जब तक आप इसके साथ काम करने के लिए सही निर्देशों का उपयोग नहीं करते हैं।
बिली ओले

जब कंटेनर-अज्ञेयवादी ओ (एन ^ 2) जटिलता का कारण बनता है (और आपके उदाहरण में यह करता है, जैसा कि सूची <>) के लिए दूरी () हे (एन) है, तो मुझे यकीन नहीं है कि यह एक फायदा है :-(
नहीं-बग्स हरे

@ नहीं-बग्सरे यही बात ठीक है: हम निश्चित नहीं हो सकते। यदि ओपी में कुछ तत्व हैं, तो शायद यह बहुत अच्छा है। अगर उसके पास लाखों हैं, तो शायद इतना भी नहीं है। केवल प्रोफाइलिंग अंत में बता सकता है लेकिन अच्छी खबर यह है: आप हमेशा बनाए रखने योग्य कोड का अनुकूलन कर सकते हैं!
२०

9

आप / iterators का उपयोग करते हैं कर सकते हैं नहीं होगा नहीं और अगर आप / नहीं का उपयोग नहीं होगा सकते हैं std::size_tपाश सूचकांक के लिए, एक बनाने .size()के लिए intहै कि दस्तावेजों धारणा और रूपांतरण स्पष्ट रूप से संकलक चेतावनी मौन करने करता रूपांतरण समारोह।

#include <cassert>
#include <cstddef>
#include <limits>

// When using int loop indexes, use size_as_int(container) instead of
// container.size() in order to document the inherent assumption that the size
// of the container can be represented by an int.
template <typename ContainerType>
/* constexpr */ int size_as_int(const ContainerType &c) {
    const auto size = c.size();  // if no auto, use `typename ContainerType::size_type`
    assert(size <= static_cast<std::size_t>(std::numeric_limits<int>::max()));
    return static_cast<int>(size);
}

फिर आप अपने छोरों को इस तरह लिखते हैं:

for (int i = 0; i < size_as_int(things); ++i) { ... }

इस फ़ंक्शन टेम्पलेट की तात्कालिकता लगभग निश्चित रूप से इनलेट होगी। डिबग बिल्ड में, धारणा की जाँच की जाएगी। रिलीज़ बिल्ड में, यह नहीं होगा और कोड उतना ही तेज़ होगा जितना कि आपने सीधे आकार () कहा था। न तो संस्करण एक संकलक चेतावनी का उत्पादन करेगा, और यह केवल मुहावरेदार पाश के लिए एक मामूली संशोधन है।

यदि आप रिलीज़ संस्करण में भी मानने में विफलताओं को पकड़ना चाहते हैं, तो आप दावे को एक बयान से बदल सकते हैं जो कुछ ऐसा फेंकता है std::out_of_range("container size exceeds range of int")

ध्यान दें कि यह हस्ताक्षरित / अहस्ताक्षरित तुलना के साथ-साथ संभावित sizeof(int)! = sizeof(Container::size_type)समस्या दोनों को हल करता है। आप अपनी सभी चेतावनियों को सक्षम छोड़ सकते हैं और अपने कोड के अन्य भागों में वास्तविक बग्स को पकड़ने के लिए उनका उपयोग कर सकते हैं।


6

आप उपयोग कर सकते हैं:

  1. size_t प्रकार, चेतावनी संदेश निकालने के लिए
  2. पुनरावृत्तियाँ + दूरी (जैसे पहले संकेत हैं)
  3. केवल पुनरावृत्तियों
  4. कार्य वस्तु

उदाहरण के लिए:

// simple class who output his value
class ConsoleOutput
{
public:
  ConsoleOutput(int value):m_value(value) { }
  int Value() const { return m_value; }
private:
  int m_value;
};

// functional object
class Predicat
{
public:
  void operator()(ConsoleOutput const& item)
  {
    std::cout << item.Value() << std::endl;
  }
};

void main()
{
  // fill list
  std::vector<ConsoleOutput> list;
  list.push_back(ConsoleOutput(1));
  list.push_back(ConsoleOutput(8));

  // 1) using size_t
  for (size_t i = 0; i < list.size(); ++i)
  {
    std::cout << list.at(i).Value() << std::endl;
  }

  // 2) iterators + distance, for std::distance only non const iterators
  std::vector<ConsoleOutput>::iterator itDistance = list.begin(), endDistance = list.end();
  for ( ; itDistance != endDistance; ++itDistance)
  {
    // int or size_t
    int const position = static_cast<int>(std::distance(list.begin(), itDistance));
    std::cout << list.at(position).Value() << std::endl;
  }

  // 3) iterators
  std::vector<ConsoleOutput>::const_iterator it = list.begin(), end = list.end();
  for ( ; it != end; ++it)
  {
    std::cout << (*it).Value() << std::endl;
  }
  // 4) functional objects
  std::for_each(list.begin(), list.end(), Predicat());
}

3

मैं C ++ 11 के लिए निम्नलिखित समाधान भी प्रस्तावित कर सकता हूं।

for (auto p = 0U; p < sys.size(); p++) {

}

(C ++ ऑटो p = 0 के लिए पर्याप्त स्मार्ट नहीं है, इसलिए मुझे p = 0U .... लगाना होगा)


1
C ++ 11 के लिए +1। जब तक एक अच्छा कारण नहीं है कि आप C ++ 11 का उपयोग नहीं कर सकते, मुझे लगता है कि नई सुविधाओं का उपयोग करना सबसे अच्छा है ... वे एक बड़ी मदद के लिए हैं। और निश्चित रूप से उपयोग करें for (auto thing : vector_of_things)यदि आपको वास्तव में सूचकांक की आवश्यकता नहीं है।
पार्कर.सिकंद

लेकिन यह केवल हस्ताक्षर की समस्या को हल करता है। यदि यह size()अहस्ताक्षरित इंट की तुलना में बड़ा है, जो बेहद सामान्य है, तो यह मदद नहीं करता है ।
एड्रियन मैक्कार्थी

3

मैं आपको एक बेहतर विचार दूंगा

for(decltype(things.size()) i = 0; i < things.size(); i++){
                   //...
}

decltype है

एक घोषित इकाई या अभिव्यक्ति के प्रकार और मूल्य श्रेणी का निरीक्षण करता है।

तो, यह प्रकार घटाता है things.size()और iएक प्रकार के रूप में ही होगा things.size()। तो, i < things.size()बिना किसी चेतावनी के निष्पादित किया जाएगा


0

मुझे भी ऐसी ही समस्या का समाधान करना पड़ा था। Size_t का उपयोग करना काम नहीं कर रहा था। मैंने दूसरी कोशिश की जो मेरे लिए काम करती है। (नीचे के अनुसार)

for(int i = things.size()-1;i>=0;i--)
{
 //...
}

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.