फंक्शन रोकना const std :: string और स्वीकार करने से 0


97

एक हजार शब्दों के लायक:

#include<string>
#include<iostream>

class SayWhat {
    public:
    SayWhat& operator[](const std::string& s) {
        std::cout<<"here\n"; // To make sure we fail on function entry
        std::cout<<s<<"\n";
        return *this;
    }
};

int main() {
    SayWhat ohNo;
    // ohNo[1]; // Does not compile. Logic prevails.
    ohNo[0]; // you didn't! this compiles.
    return 0;
}

संकलक को स्ट्रिंग स्वीकार करने वाले ब्रैकेट संचालक को नंबर 0 से पास करने पर कंपाइलर शिकायत नहीं कर रहा है। इसके बजाय, यह विधि के साथ प्रवेश करने से पहले संकलित करता है और विफल रहता है:

terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_S_construct null not valid

सन्दर्भ के लिए:

> g++ -std=c++17 -O3 -Wall -Werror -pedantic test.cpp -o test && ./test
> g++ --version
gcc version 7.3.1 20180303 (Red Hat 7.3.1-5) (GCC)

मेरा अनुमान

संकलक का उपयोग विधायक द्वारा std::string(0)विधि में प्रवेश करने के लिए किया जाता है, जो बिना किसी अच्छे कारण के एक ही समस्या (उपरोक्त त्रुटि को गूगल करता है) पैदा करता है।

सवाल

क्या क्लास की तरफ से इसे ठीक करने के लिए कुछ भी है, इसलिए एपीआई उपयोगकर्ता को यह महसूस नहीं होता है और संकलन समय पर त्रुटि का पता लगाया जाता है?

यही है, एक अधिभार जोड़ना

void operator[](size_t t) {
    throw std::runtime_error("don't");
}

एक अच्छा समाधान नहीं है।


2
संकलित कोड, ओएनएनओ में दृश्य स्टूडियो में अपवाद फेंकना [0] अपवाद के साथ "0xC0000005: एक्सेस उल्लंघन पढ़ने के स्थान 0x00000000"
ट्रुथसेकर

5
एक निजी अधिभार की घोषणा करना operator[]()एक intतर्क को स्वीकार करता है, और इसे परिभाषित नहीं करता है।
पीटर

2
@Peter हालांकि यह ध्यान दें कि यह एक लिंकर त्रुटि है, जो अभी भी मेरे पास से बेहतर है।
कबनस

5
@kabanus उपरोक्त परिदृश्य में, यह एक संकलक त्रुटि होगी, क्योंकि ऑपरेटर निजी है! लिंकर त्रुटि केवल अगर वर्ग के भीतर ...
Aconcagua

5
@Peter कि स्थितियों में जहां कोई सी ++ 11 उपलब्ध है में विशेष रूप से दिलचस्प है - और इन करते हैं आज भी मौजूद हैं (वास्तव में मैं एक परियोजना से निपटने के लिए होने में हूं, और मैं काफी कुछ नई सुविधाओं याद कर रहा हूँ ... )।
एकॉनगुआ

जवाबों:


161

कारण std::string(0)वैध है, 0अशक्त सूचक स्थिर होने के कारण है । तो 0 पॉइंटर लेने वाले स्ट्रिंग कंस्ट्रक्टर से मेल खाता है। फिर कोड पूर्व शर्त से चलता है कि कोई नल पॉइंटर पास नहीं कर सकता है std::string

केवल शाब्दिक 0रूप से एक अशक्त सूचक स्थिरांक के रूप में व्याख्या की जाएगी, अगर यह एक रन टाइम मूल्य था, तो intआपको यह समस्या नहीं होगी (क्योंकि तब अधिभार संकल्प intइसके बजाय एक रूपांतरण की तलाश में होगा )। और न ही शाब्दिक 1समस्या है, क्योंकि 1अशक्त सूचक स्थिर नहीं है।

चूंकि यह एक संकलन समय समस्या है (शाब्दिक अमान्य मूल्य) आप इसे संकलन समय पर पकड़ सकते हैं। इस फॉर्म का एक अधिभार जोड़ें:

void operator[](std::nullptr_t) = delete;

std::nullptr_tका प्रकार है nullptr। और यह से मेल खाएगी किसी भी नल पॉइंटर निरंतर, यह हो 0, 0ULLया nullptr। और चूंकि फ़ंक्शन हटा दिया गया है, यह अधिभार संकल्प के दौरान एक संकलन समय त्रुटि का कारण होगा।


यह अब तक का सबसे अच्छा समाधान है, पूरी तरह से भूल गया कि मैं एक NULL पॉइंटर को ओवरलोड कर सकता हूं।
कबानुस

दृश्य स्टूडियो में, यहां तक ​​कि "ओहो [0]" एक शून्य मान अपवाद को फेंक रहा है। क्या इसका मतलब std :: string class का कार्यान्वयन विशिष्ट है?
ट्रूथसेकर

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

18
@pmp - एक गंदे सूचक को पास करने std::stringकी अनुमति C ++ मानक द्वारा नहीं है। यह अपरिभाषित व्यवहार है, इसलिए MSVC जो चाहे उसे कर सकता है (जैसे एक अपवाद को फेंक दें)।
स्टोरीटेलर - Unslander Monica

26

एक विकल्प के लिए एक privateअधिभार की घोषणा हैoperator[]() एक अभिन्न तर्क स्वीकार किया जाए, और इसे परिभाषित न करें।

यह विकल्प जैसे सभी विकल्पों के विपरीत सभी C ++ मानकों (1998 पर) के साथ काम करेगा void operator[](std::nullptr_t) = delete जो कि C ++ 11 से मान्य ।

operator[]()ए बना रहा हैprivate सदस्य अपने उदाहरण पर एक निदान त्रुटि का कारण होगा ohNo[0], जब तक कि अभिव्यक्ति एक सदस्य समारोह से या प्रयोग किया जाता है, friendवर्ग के।

यदि उस अभिव्यक्ति का उपयोग किसी सदस्य फ़ंक्शन या friendवर्ग से किया जाता है , तो कोड संकलित करेगा लेकिन - क्योंकि फ़ंक्शन परिभाषित नहीं है - आम तौर पर बिल्ड विफल हो जाएगा (जैसे एक अपरिभाषित फ़ंक्शन के कारण लिंकर त्रुटि)।

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