C ++ में संदर्भ द्वारा गुजरते समय एक पैरामीटर के लिए डिफ़ॉल्ट मान


117

क्या हम किसी फ़ंक्शन के पैरामीटर को डिफ़ॉल्ट मान देना संभव है जबकि हम संदर्भ द्वारा पैरामीटर पारित कर रहे हैं। C ++ में

उदाहरण के लिए, जब मैं एक समारोह घोषित करने की कोशिश करता हूं:

virtual const ULONG Write(ULONG &State = 0, bool sequence = true);

जब मैं ऐसा करता हूं तो यह एक त्रुटि देता है:

C2440 त्रुटि: 'डिफ़ॉल्ट तर्क': 'कॉन्स्टेंट इंट' से 'अहस्ताक्षरित लंबे और' एक संदर्भ को 'कॉन्स्टेबल' में परिवर्तित नहीं किया जा सकता, एक गैर-अंतराल के लिए बाध्य नहीं किया जा सकता


96
दया से, हम Google शैली मार्गदर्शिका से बाध्य नहीं हैं।

23
"ऐसा मत करो। Google शैली गाइड (और अन्य) संदर्भ द्वारा गैर-कॉन्स्टल पास पर प्रतिबंध लगाते हैं" मुझे लगता है कि स्टाइल गाइड को कई व्यक्तिपरक भागों को शामिल करने के लिए जाना जाता है। यह उनमें से एक जैसा दिखता है।
जोहान्स शाउब -

13
WxWidgets style guide says "don't use templates" and they have good reasons<- स्क्रू एसटीडी :: वेक्टर, मैं कहता हूं
जोहान्स शाउब -

7
@ जैफामफोन: गूगल स्टाइल गाइड भी धाराओं का उपयोग नहीं करने के लिए कहता है। क्या आप उन्हें भी टालने का सुझाव देंगे?
rlbond

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

जवाबों:


103

आप इसे एक कॉन्स्ट रेफरेंस के लिए कर सकते हैं, लेकिन नॉन-कॉस्ट वाले के लिए नहीं। ऐसा इसलिए है क्योंकि C ++ एक अस्थायी (इस मामले में डिफ़ॉल्ट मान) को गैर-कॉन्स्टेबल संदर्भ के लिए बाध्य नहीं होने देता है।

एक तरीका यह है कि डिफ़ॉल्ट के रूप में एक वास्तविक उदाहरण का उपयोग किया जाएगा:

static int AVAL = 1;

void f( int & x = AVAL ) {
   // stuff
} 

int main() {
     f();       // equivalent to f(AVAL);
}

लेकिन यह बहुत सीमित व्यावहारिक उपयोग है।


अगर मैं इसे कांस्ट करता हूं, तो क्या यह मुझे फंक्शन के लिए एक अलग पता देने की अनुमति देगा? या राज्य का पता हमेशा 0 होगा और इतना निरर्थक?

यदि आप संदर्भ का उपयोग कर रहे हैं, तो आप पते नहीं दे रहे हैं।

3
बढ़ावा :: बचाव शून्य च के लिए सरणी (पूर्णांक और एक्स = बढ़ावा :: सरणी <int, 1> () [0]) {} .. :)
Johannes Schaub - litb

1
यदि पते नहीं हैं तो वास्तव में क्या हो रहा है?

2
@ कोई संदर्भ। इसे एक पते के रूप में सोचना wriong है। यदि आप एक पता चाहते हैं, तो एक सूचक का उपयोग करें।

33

यह आपके उत्तर के लिए पहले से ही एक सीधी टिप्पणी में कहा गया है, लेकिन इसे आधिकारिक रूप से बताने के लिए। आप जो उपयोग करना चाहते हैं वह एक अधिभार है:

virtual const ULONG Write(ULONG &State, bool sequence);
inline const ULONG Write()
{
  ULONG state;
  bool sequence = true;
  Write (state, sequence);
}

फ़ंक्शन ओवरलोड का उपयोग करने से अतिरिक्त लाभ भी होते हैं। सबसे पहले आप अपनी इच्छानुसार किसी भी तर्क को डिफ़ॉल्ट कर सकते हैं:

class A {}; 
class B {}; 
class C {};

void foo (A const &, B const &, C const &);
void foo (B const &, C const &); // A defaulted
void foo (A const &, C const &); // B defaulted
void foo (C const &); // A & B defaulted etc...

व्युत्पन्न वर्ग में आभासी कार्यों के लिए डिफ़ॉल्ट तर्कों को फिर से परिभाषित करना भी संभव है, जो ओवरलोडिंग से बचा जाता है:

class Base {
public:
  virtual void f1 (int i = 0);  // default '0'

  virtual void f2 (int);
  inline void f2 () {
    f2(0);                      // equivalent to default of '0'
  }
};

class Derived : public Base{
public:
  virtual void f1 (int i = 10);  // default '10'

  using Base::f2;
  virtual void f2 (int);
};

void bar ()
{
  Derived d;
  Base & b (d);
  d.f1 ();   // '10' used
  b.f1 ();   // '0' used

  d.f2 ();   // f1(int) called with '0' 
  b.f2 ();   // f1(int) called with '0
}

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


19
कुछ लोगों को लगता है कि एक डिफाल्ट-पैरामम, ओवरलोड के विशाल गुणा से कम भयानक है।
श्री बॉय

2
शायद अंत में, आप उल्लेख करते हैं: d.f2(); // f2(int) called with '0' b.f2(); // f2(int) called with '0'
पिएत्रो

4
अंतिम विवरण पर: C ++ 11 से, आप कोड डुप्लीकेशन से बचने के लिए एक से दूसरे कंस्ट्रक्टर को कॉल करने के लिए डेलिगेटिंग कंस्ट्रक्टर्स का उपयोग कर सकते हैं।
फ्रेड स्कोन नोव

25

अभी भी वैकल्पिक तर्क प्रदान करने का पुराना C तरीका है: एक सूचक जो मौजूद नहीं होने पर NULL हो सकता है:

void write( int *optional = 0 ) {
    if (optional) *optional = 5;
}

मुझे यह विधि बहुत पसंद है, बहुत छोटी और सरल। सुपर व्यावहारिक यदि कभी-कभी आप कुछ अतिरिक्त जानकारी, आँकड़े आदि वापस करना चाहते हैं, जिनकी आपको आमतौर पर आवश्यकता नहीं होती है।
१४

11

यह छोटा सा टेम्पलेट आपकी मदद करेगा:

template<typename T> class ByRef {
public:
    ByRef() {
    }

    ByRef(const T value) : mValue(value) {
    }

    operator T&() const {
        return((T&)mValue);
    }

private:
    T mValue;
};

तब आप कर पाएंगे:

virtual const ULONG Write(ULONG &State = ByRef<ULONG>(0), bool sequence = true);

ByRefलाइव, मेमोरी-वार का तात्कालिकता कहाँ है ? क्या यह एक अस्थायी वस्तु नहीं है जो कुछ दायरे (जैसे निर्माणकर्ता) को छोड़ने पर नष्ट हो जाएगी?
एंड्रयू चींग

1
@AndrewCheong इसकी पूरी मंशा इन-स्पॉट का निर्माण करना और लाइन पूरी होने पर नष्ट होना है। यह एक कॉल की अवधि के लिए एक संदर्भ को उजागर करने का एक साधन है ताकि एक डिफ़ॉल्ट पैरामीटर को तब भी प्रदान किया जा सके जब वह किसी संदर्भ की अपेक्षा करता है। इस कोड का उपयोग एक सक्रिय परियोजना में किया जाता है और अपेक्षित रूप से कार्य करता है।
माइक वियर

7

नहीं, यह संभव नहीं है।

संदर्भ द्वारा पासिंग का अर्थ है कि फ़ंक्शन पैरामीटर का मान बदल सकता है। यदि पैरामीटर कॉलर द्वारा प्रदान नहीं किया जाता है और डिफ़ॉल्ट स्थिरांक से आता है, तो फ़ंक्शन को क्या बदलना है?


3
पारंपरिक FORTRAN तरीका 0 के मान को बदलना होगा, लेकिन C ++ में ऐसा नहीं होता है।
डेविड थॉर्नले

7

संदर्भ द्वारा तर्क पास करने के दो कारण हैं: (1) प्रदर्शन के लिए (जिस स्थिति में आप संदर्भ द्वारा पास करना चाहते हैं) और (2) क्योंकि आपको फ़ंक्शन के अंदर तर्क के मूल्य को बदलने की क्षमता चाहिए।

मुझे बहुत संदेह है कि आधुनिक आर्किटेक्चर पर एक अहस्ताक्षरित लंबे समय से गुजरना आपको बहुत धीमा कर रहा है। इसलिए मैं मान रहा हूं कि आप Stateविधि के अंदर के मूल्य को बदलना चाहते हैं । कंपाइलर शिकायत कर रहा है क्योंकि स्थिरांक 0को बदला नहीं जा सकता है, क्योंकि यह एक त्रुटि (त्रुटि संदेश में "गैर-लंबू") और विनिमेय ( constत्रुटि संदेश में) है।

सीधे शब्दों में कहें, तो आप एक ऐसा तरीका चाहते हैं, जो पारित किए गए तर्क को बदल सके, लेकिन डिफ़ॉल्ट रूप से आप एक तर्क को पारित करना चाहते हैं जो बदल नहीं सकता।

इसे दूसरे तरीके से रखने के लिए, गैर- constसंदर्भों को वास्तविक चर का संदर्भ देना होगा। फ़ंक्शन हस्ताक्षर में डिफ़ॉल्ट मान ( 0) वास्तविक चर नहीं है। आप उसी समस्या में भाग रहे हैं:

struct Foo {
    virtual ULONG Write(ULONG& State, bool sequence = true);
};

Foo f;
ULONG s = 5;
f.Write(s); // perfectly OK, because s is a real variable
f.Write(0); // compiler error, 0 is not a real variable
            // if the value of 0 were changed in the function,
            // I would have no way to refer to the new value

यदि आप वास्तव में Stateइस पद्धति के अंदर बदलने का इरादा नहीं रखते हैं , तो आप इसे बदल सकते हैं const ULONG&। लेकिन आपको इससे बड़ा प्रदर्शन लाभ नहीं मिलने वाला है, इसलिए मैं इसे गैर-संदर्भ में बदलने की सलाह दूंगा ULONG। मुझे लगता है कि आप पहले से ही एक लौट रहे हैं ULONG, और मुझे एक संदेह है कि Stateकिसी भी आवश्यक संशोधनों के बाद इसका मूल्य है । जिस स्थिति में मैं बस विधि को घोषित करूंगा:

// returns value of State
virtual ULONG Write(ULONG State = 0, bool sequence = true);

बेशक, मुझे पूरा यकीन नहीं है कि आप क्या लिख ​​रहे हैं या कहाँ हैं। लेकिन यह एक और समय के लिए एक और सवाल है।


6

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

int* foo (int& i )
{
   return &i;
}

foo(0); // compiler error.

const int* bar ( const int& i )
{
   return &i;
}

bar(0); // ok.

सुनिश्चित करें कि आप डिफ़ॉल्ट मान वाले पते पर हैं और आप ठीक हैं।

int null_object = 0;

int Write(int &state = null_object, bool sequence = true)
{
   if( &state == &null_object )
   {
      // called with default paramter
      return sequence? 1: rand();
   }
   else
   {
      // called with user parameter
      state += sequence? 1: rand();
      return state;
   }
}

मैंने इस पैटर्न का कुछ बार उपयोग किया है जहां मेरे पास एक पैरामीटर था जो एक चर या अशक्त हो सकता है। नियमित दृष्टिकोण यह है कि यह एक संकेतक में उपयोगकर्ता पास है। यदि वे नहीं चाहते कि वे आपको मान में भरें, तो वे एक NULL पॉइंटर में पास होते हैं। मुझे ऑब्जेक्ट एप्रोच करना पसंद है। यह कॉलली कोड को बहुत जटिल किए बिना कॉलर्स के जीवन को आसान बनाता है।


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

1

मुझे नहीं लगता है, और इसका कारण यह है कि डिफ़ॉल्ट मानों को स्थिरांक के लिए मूल्यांकन किया जाता है और संदर्भ द्वारा पारित मूल्यों को बदलने में सक्षम होना चाहिए, जब तक कि आप इसे निरंतर संदर्भ घोषित न करें।


2
डिफ़ॉल्ट मानों का मूल्यांकन "स्थिरांक के रूप में नहीं किया जाता है"।

1

एक और तरीका निम्नलिखित हो सकता है:

virtual const ULONG Write(ULONG &State, bool sequence = true);

// wrapper
const ULONG Write(bool sequence = true)
{
   ULONG dummy;
   return Write(dummy, sequence);
}

तब निम्नलिखित कॉल संभव हैं:

ULONG State;
object->Write(State, false); // sequence is false, "returns" State
object->Write(State); // assumes sequence = true, "returns" State
object->Write(false); // sequence is false, no "return"
object->Write(); // assumes sequence = true, no "return"

1
void f(const double& v = *(double*) NULL)
{
  if (&v == NULL)
    cout << "default" << endl;
  else
    cout << "other " << v << endl;
}

यह काम करता हैं। मूल रूप से, यह NULL पॉइंटिंग संदर्भ की जाँच करने के लिए मान-पर-संदर्भ तक पहुँचना है (तार्किक रूप से कोई NULL-reference नहीं है, केवल यह कि आप जो बिंदु NULL है)। ऊपर, यदि आप कुछ पुस्तकालय का उपयोग कर रहे हैं, जो "संदर्भ" पर काम कर रहा है, तो आमतौर पर पुस्तकालय विशिष्ट संदर्भ चर के लिए ऐसा करने के लिए कुछ एपीआई जैसे "नल" () होता है। और ऐसे मामलों में उन एपीआई का उपयोग करने का सुझाव दिया गया है।
परवश

1

OO के मामले में ... यह कहने के लिए कि एक गिविंग क्लास है और "डिफ़ॉल्ट" का अर्थ है कि इस डिफ़ॉल्ट (मान) को एकांत में घोषित किया जाना चाहिए, फिर डिफ़ॉल्ट पैरामीटर पूर्व के रूप में usd हो सकता है:

class Pagination {
public:
    int currentPage;
    //...
    Pagination() {
        currentPage = 1;
        //...
    }
    // your Default Pagination
    static Pagination& Default() {
        static Pagination pag;
        return pag;
    }
};

अपने तरीके पर ...

 shared_ptr<vector<Auditoria> > 
 findByFilter(Auditoria& audit, Pagination& pagination = Pagination::Default() ) {

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


1

यह राज्य के लिए कांस्ट क्वालिफायर के साथ संभव है:

virtual const ULONG Write(const ULONG &State = 0, bool sequence = true);

यह एक बिंदु के रूप में एक लंबा रेफरी पास करने के लिए व्यर्थ और यहां तक ​​कि हास्यास्पद है, और ओपी जो चाहता है वह हासिल नहीं करता है।
जिम बेल्टर


0

इसके लिए भी गंदी चाल है:

virtual const ULONG Write(ULONG &&State = 0, bool sequence = true);

इस मामले में आपको इसे कॉल करना होगा std::move:

ULONG val = 0;
Write(std::move(val));

यह केवल कुछ मज़ेदार काम है, मैं पूरी तरह से वास्तविक कोड का उपयोग करने की सलाह नहीं देता!


0

इसके लिए मेरे पास एक वर्कअराउंड है, निम्न उदाहरण के लिए डिफ़ॉल्ट मान देखें int&:

class Helper
{
public:
    int x;
    operator int&() { return x; }
};

// How to use it:
void foo(int &x = Helper())
{

}

आप इसे किसी भी तुच्छ डेटा प्रकार के लिए कर सकते हैं, जैसे कि bool, double...


0

2 अधिभार कार्यों को परिभाषित करें।

virtual const ULONG Write(ULONG &State, bool sequence = true);

virtual const ULONG Write(bool sequence = true)
{
    int State = 0;
    return Write(State, sequence);
}

-3

वर्चुअल कॉन्स्ट यूलोंग लिखो (उलॉन्ग एंड स्टेट = 0, बूल अनुक्रम = सच);

उत्तर काफी सरल है और मैं समझाने पर इतना अच्छा नहीं हूं लेकिन अगर आप एक गैर-कास्ट पैरामीटर के लिए एक डिफ़ॉल्ट मान पास करना चाहते हैं जो संभवतः इस फ़ंक्शन में संशोधित किया जाएगा, तो इसे इस तरह उपयोग करना है:

virtual const ULONG Write(ULONG &State = *(ULONG*)0, bool sequence =
> true);

3
NULL पॉइंटर को Derefer करना अवैध है। यह कुछ मामलों में काम कर सकता है, लेकिन यह अवैध है। यहाँ और अधिक पढ़ें parashift.com/c++-faq-lite/references.html#faq-8.7
Spo1ler
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.