अस्थायी लोगों को कैसे अस्वीकार करें


107

एक वर्ग फू के लिए, क्या इसे नाम दिए बिना इसका निर्माण करने से इनकार करने का एक तरीका है?

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

Foo("hi");

और केवल इसे अनुमति दें यदि आप इसे निम्नलिखित की तरह एक नाम दें?

Foo my_foo("hi");

पहले एक का जीवनकाल सिर्फ बयान है, और दूसरा एक संलग्न ब्लॉक है। मेरे उपयोग के मामले में, Fooनिर्माता और विध्वंसक के बीच के समय को माप रहा है। चूंकि मैं कभी भी स्थानीय चर का उल्लेख नहीं करता, इसलिए मैं अक्सर इसे रखना भूल जाता हूं और गलती से जीवनकाल बदल देता हूं। मैं इसके बजाय एक संकलन समय त्रुटि प्राप्त करना चाहता हूं।


8
यह म्यूटेक्स लॉक गार्ड के लिए भी काम आ सकता है।
लुकास क्लीमेंट

1
ठीक है, आप अपना खुद का सी ++ संकलक लिख सकते हैं , जहां यह निषिद्ध था, लेकिन कड़ाई से बोलने पर यह सी ++ नहीं होगा। ऐसी जगहें भी हैं, जहां अस्थायी चीजें उपयोगी होंगी, जैसे कि किसी वस्तु को उदाहरण के लिए किसी फ़ंक्शन से वापस करते समय (जैसे return std::string("Foo");)
कुछ प्रोग्रामर

2
नहीं, आप ऐसा नहीं कर सकते, क्षमा करें
आर्मेन सुनायरन

2
आपके धर्म के आधार पर यह एक ऐसा मामला हो सकता है, जहां मैक्रोज़ काम में आ सकते हैं (usnig द्वारा, केवल एक मैक्रो के माध्यम से जो हमेशा एक परिवर्तनशील बनाता है)
PlasmaHH

3
कुछ ऐसा लगता है कि मैं अपने LINT उपकरण को किसी ऐसी चीज़ से पकड़ना चाहूंगा, जिसे मैं संकलक हैक द्वारा आसानी से रोकना चाहता हूं।
वॉरेन पी

जवाबों:


101

एक और मैक्रो-आधारित समाधान:

#define Foo class Foo

बयान का Foo("hi");विस्तार है class Foo("hi");, जो बीमार है; लेकिन Foo a("hi")विस्तार है class Foo a("hi"), जो सही है।

इसका यह लाभ है कि यह स्रोत (और सही) कोड के साथ दोनों बाइनरी-संगत है। (यह दावा पूरी तरह सही नहीं है - कृपया जोहानस शाउब की टिप्पणी और आगामी चर्चा नीचे देखें: "आप कैसे जान सकते हैं कि यह मौजूदा कोड के साथ स्रोत के अनुकूल है? उसके दोस्त में उसका हेडर शामिल है और शून्य है () {int Foo = 0}; जो पहले ठीक संकलित था और अब गलतफहमी! इसके अलावा, हर पंक्ति जो कक्षा फू के एक सदस्य समारोह को परिभाषित करती है वह विफल हो जाती है: शून्य वर्ग फू :: बार () {} " )


51
आप यह कैसे जान सकते हैं कि यह मौजूदा कोड के साथ संगत है? उनके दोस्त में उनके हेडर शामिल हैं और void f() { int Foo = 0; }जो पहले ठीक संकलित थे और अब गलत! इसके अलावा, हर पंक्ति जो कक्षा फू के एक सदस्य फ़ंक्शन को परिभाषित करती है वह विफल हो जाती है void class Foo::bar() {}:।
जोहान्स शाउब -

21
इसे इतने वोट कैसे मिल सकते हैं? बस @ जोहान्सचैब-लिट द्वारा टिप्पणी को देखें और आप समझ जाएंगे कि यह वास्तव में बुरा समाधान है। चूँकि सदस्य कार्यों की सभी परिभाषाएँ इसके बाद अमान्य हैं .. -1 मेरी ओर से
आमिर

2
@JustMaximumPower: मुझे आशा है कि व्यंग्यात्मक था क्योंकि यदि नहीं, तो यह फिर से एक बुरा (बुरा पढ़ें) है। क्योंकि हम इसे अनडिफाइन करने के बाद एक वर्ग में आते हैं, जिसका मतलब है कि आपको एक समान लाइन पर संकलन त्रुटि (जो कि ओपी का इरादा है) नहीं मिलेगी, यानी Foo("Hi")अब Foo.cpp के अंदर
आमिर

1
@ आमिर नहीं मैं गंभीर हूं। मार्टिन सी। मार्टिन का इरादा फू के कार्यान्वयन की रक्षा के लिए इसका उपयोग करना है न कि कार्यान्वयन का।
JustMaximumPower

1
मैंने विजुअल स्टूडियो 2012 में कोशिश की और पाया class Foo("hi");कि संकलन के लिए ठीक है।
भित्तिचित्र

71

थोड़ा हैक के बारे में कैसे

class Foo
{
    public:
        Foo (const char*) {}
};

void Foo (float);


int main ()
{
    Foo ("hello"); // error
    class Foo a("hi"); // OK
    return 1;
}

1
महान हैक! एक नोट: Foo a("hi");(बिना class) भी एक त्रुटि होगी।
bitmask

मुझे यकीन नहीं कि मैं समझा हूँ। फू ("हैलो") शून्य फू (फ्लोट) को कॉल करने की कोशिश करता है और इसके परिणामस्वरूप लिंकर त्रुटि होती है? लेकिन फ्लोट संस्करण को फू कॉटर के बजाय क्यों कहा जाता है?
अन्नू

2
undu, hm क्या संकलक आप उपयोग कर रहे हैं? gcc 3.4 की शिकायत है कि फ्लोट में कोई रूपांतरण नहीं है। यह एक फ़ंक्शन को कॉल करने का प्रयास करता है Fooक्योंकि यह एक वर्ग पर एक मिसाल लेता है।

@aleguna वास्तव में मैंने इस कोड को चलाने की कोशिश नहीं की, यह सिर्फ एक (बुरा) अनुमान था: एस लेकिन आपने मेरे सवाल का वैसे भी जवाब दिया, मुझे नहीं पता था कि फ़ंक्शन क्लास से अधिक पूर्वता लेता है।
16

1
@didierc नहीं, Foo::Foo("hi")C ++ में अस्वीकृत है।
जोहानस शाउब -

44

कंस्ट्रक्टर को निजी बनाएं लेकिन कक्षा को एक विधि बनाएं


9
-1: यह ओपी की समस्या को कैसे हल करता है? तुम अब भी लिख सकते हैं Foo::create();भर मेंFoo const & x = Foo::create();
थॉमस का संपादन करने

@ThomasEding मुझे लगता है कि आप सही हैं, यह ओपी की मुख्य समस्या को ठीक नहीं करता है, लेकिन सिर्फ उसे सोचने के लिए मजबूर करता है और वह गलती नहीं करता है जो वह कर रहा है।
22

1
@ThomasEding आप अपने आप को उन नाराज उपयोगकर्ताओं से सुरक्षित नहीं रख सकते जो सिस्टम को तोड़ना चाहते हैं। यहां तक ​​कि @ ecatmur के हैक के साथ भी आप कह सकते हैं std::common_type<Foo>::type()और आपको एक अस्थायी मिलता है। या भी typedef Foo bar; bar()
जोहान्स शाउब - २१:३ub पर

@ जोहान्सचैब-लिटब: लेकिन बड़ा अंतर यह है कि यह गलती से था या नहीं। लगभग कोई रास्ता नहीं टाइपिंग std::common_type<Foo>::type()गलती से है। Foo const & x = ...दुर्घटना से बाहर निकलना पूरी तरह से विश्वसनीय है।
थॉमस ईडिंग

24

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

जिस भी कंस्ट्रक्टर को आप संरक्षित करना चाहते हैं, उसे एक डिफ़ॉल्ट तर्क की आवश्यकता होती set(guard)है, जिसे कहा जाता है।

struct Guard {
  Guard()
    :guardflagp()
  { }

  ~Guard() {
    assert(guardflagp && "Forgot to call guard?");
    *guardflagp = 0;
  }

  void *set(Guard const *&guardflag) {
    if(guardflagp) {
      *guardflagp = 0;
    }

    guardflagp = &guardflag;
    *guardflagp = this;
  }

private:
  Guard const **guardflagp;
};

class Foo {
public:
  Foo(const char *arg1, Guard &&g = Guard()) 
    :guard()
  { g.set(guard); }

  ~Foo() {
    assert(!guard && "A Foo object cannot be temporary!");
  }

private:
  mutable Guard const *guard;
}; 

विशेषताएं हैं:

Foo f() {
  // OK (no temporary)
  Foo f1("hello");

  // may throw (may introduce a temporary on behalf of the compiler)
  Foo f2 = "hello";

  // may throw (introduces a temporary that may be optimized away
  Foo f3 = Foo("hello");

  // OK (no temporary)
  Foo f4{"hello"};

  // OK (no temporary)
  Foo f = { "hello" };

  // always throws
  Foo("hello");

  // OK (normal copy)
  return f;

  // may throw (may introduce a temporary on behalf of the compiler)
  return "hello";

  // OK (initialized temporary lives longer than its initializers)
  return { "hello" };
}

int main() {
  // OK (it's f that created the temporary in its body)
  f();

  // OK (normal copy)
  Foo g1(f());

  // OK (normal copy)
  Foo g2 = f();
}

के मामले में f2, f3और वापसी की "hello"इच्छा नहीं हो सकती है। फेंकने से रोकने के लिए, आप कॉपी guardके स्रोत के बजाय अब हमें संरक्षित करने के लिए कॉपी के स्रोत को अस्थायी बना सकते हैं। अब आप यह भी देखें कि हमने ऊपर दिए गए बिंदुओं का उपयोग क्यों किया - यह हमें लचीला होने की अनुमति देता है।

class Foo {
public:
  Foo(const char *arg1, Guard &&g = Guard()) 
    :guard()
  { g.set(guard); }

  Foo(Foo &&other)
    :guard(other.guard)
  {
    if(guard) {
      guard->set(guard);
    }
  }

  Foo(const Foo& other)
    :guard(other.guard)
  {
    if(guard) {
      guard->set(guard);
    }
  }

  ~Foo() {
    assert(!guard && "A Foo object cannot be temporary!");
  }

private:
  mutable Guard const *guard;
}; 

विशेषताओं के लिए f2, f3और return "hello"अब हमेशा के लिए हैं // OK


2
Foo f = "hello"; // may throwयह मुझे इस कोड का उपयोग न करने के लिए डराने के लिए पर्याप्त है।
थॉमस ईडिंग

4
@thomas, मैं कंस्ट्रक्टर को चिह्नित करने की सलाह देता हूं explicitऔर फिर ऐसे कोड को और अधिक संकलित नहीं करता। लक्ष्य अस्थायी को धूमिल करना था, और यह करता है। यदि आप डरते हैं, तो आप इसे कॉपी के स्रोत को सेट करके फेंक नहीं सकते हैं या कंस्ट्रक्टर को एक काल्पनिक कदम बना सकते हैं। तब केवल कई प्रतियों की अंतिम वस्तु फेंक सकती है यदि यह अभी भी एक अस्थायी के रूप में समाप्त होती है।
जोहान्स शाउब -

2
हे भगवान। मैं C ++ और C ++ 11 में नौसिखिया नहीं हूं, लेकिन मैं यह नहीं समझ सकता कि यह कैसे काम करता है। क्या आप कुछ स्पष्टीकरणों को जोड़ सकते हैं? ..
मिखाइल

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

1
@ जोहान्सचैब-लिट्टी बहुत अच्छी चाल। मैंने वास्तव में सोचा था कि यह भेद करना असंभव है Foo(...);और Foo foo(...);अंदर से Foo
मिखाइल

18

कुछ साल पहले मैंने GNU C ++ कंपाइलर के लिए एक पैच लिखा था जो उस स्थिति के लिए एक नया चेतावनी विकल्प जोड़ता है। यह एक Bugzilla आइटम में ट्रैक किया गया है

दुर्भाग्य से, जीसीसी बुग्जिला एक दफन जमीन है जहां अच्छी तरह से माना पैच-शामिल फीचर सुझाव मरने के लिए जाते हैं। :)

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


9

जैसा कि, आपके कार्यान्वयन के साथ, आप ऐसा नहीं कर सकते, लेकिन आप इस नियम का उपयोग अपने लाभ के लिए कर सकते हैं:

अस्थायी वस्तुओं को गैर-कॉन्स्टेबल संदर्भों के लिए बाध्य नहीं किया जा सकता है

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

कोड नमूना

class Foo
{
    public:
        Foo(const char* ){}
        friend void InitMethod(Foo& obj);
};

void InitMethod(Foo& obj){}

int main()
{
    Foo myVar("InitMe");
    InitMethod(myVar);    //Works

    InitMethod("InitMe"); //Does not work  
    return 0;
}

उत्पादन

prog.cpp: In function int main()’:
prog.cpp:13: error: invalid initialization of non-const reference of type Foo&’ from a temporary of type const char*’
prog.cpp:7: error: in passing argument 1 of void InitMethod(Foo&)’

1
@ डिडिएरेक: बशर्ते वे एक अतिरिक्त कार्य प्रदान करें। ऐसा न करना आपके ऊपर है। हम मानक द्वारा स्पष्ट रूप से अनुमति नहीं दी गई चीज़ को प्राप्त करने के लिए एक तरह से ट्विक करने की कोशिश कर रहे हैं, इसलिए संभोग पर प्रतिबंध होगा।
आलोक सेव

@didierc पैरामीटर xएक नामित वस्तु है, इसलिए यह स्पष्ट नहीं है कि क्या हम वास्तव में इसे मना करना चाहते हैं। यदि आपके द्वारा उपयोग किया गया निर्माण स्पष्ट है, तो लोग सहज रूप से करेंगे Foo f = Foo("hello");। मुझे लगता है कि अगर वे असफल होते तो नाराज हो जाते। मेरे समाधान ने शुरू में इसे (और बहुत समान मामलों को) एक अपवाद / जोर-विफलता के साथ खारिज कर दिया और किसी ने शिकायत की।
जोहान्स शाउब - 21

@ जोहान्सचैब-लिटब हां, ओपी एक निर्माता द्वारा बाइंडिंग मजबूर करके उत्पन्न मूल्य को त्यागना चाहता है। मेरा उदाहरण गलत है।
9

7

बस एक डिफ़ॉल्ट निर्माता नहीं है, और हर निर्माता में एक उदाहरण के लिए एक संदर्भ की आवश्यकता है।

#include <iostream>
using namespace std;

enum SelfRef { selfRef };

struct S
{
    S( SelfRef, S const & ) {}
};

int main()
{
    S a( selfRef, a );
}

3
अच्छा विचार है, लेकिन जैसे ही आपके पास एक चर है S(selfRef, a);:। : /
Xeo

3
@ Xeo S(SelfRef, S const& s) { assert(&s == this); }, यदि रनटाइम त्रुटि स्वीकार्य है।

6

नहीं, मुझे डर है कि यह संभव नहीं है। लेकिन आप एक मैक्रो बनाकर समान प्रभाव प्राप्त कर सकते हैं।

#define FOO(x) Foo _foo(x)

इसके साथ, आप Foo my_foo (x) के बजाय FOO (x) लिख सकते हैं।


5
मैं उत्थान करने जा रहा था, लेकिन फिर मैंने देखा "आप एक मैक्रो बना सकते हैं"।
ग्रिवेस

1
ठीक है, अंडरस्कोर तय किया। @Griwes - एक कट्टरपंथी मत बनो। "यह नहीं किया जा सकता है" की तुलना में "मैक्रो का उपयोग करें" कहना बेहतर है।
अमोरिया

5
खैर, यह नहीं किया जा सकता है। आपने समस्या को बिल्कुल हल नहीं किया है, यह अभी भी पूरी तरह से कानूनी है Foo();
पिल्ला

11
अब आप यहाँ जिद्दी हो रहे हैं। फू क्लास को कुछ जटिल नाम दें, और मैक्रो फू को कॉल करें। समस्या सुलझ गयी।
एमोरिया

8
कुछ इस तरह:class Do_not_use_this_class_directly_Only_use_it_via_the_FOO_macro;
बेंजामिन लिंडले

4

चूंकि प्राथमिक लक्ष्य बग को रोकना है, इस पर विचार करें:

struct Foo
{
  Foo( const char* ) { /* ... */ }
};

enum { Foo };

int main()
{
  struct Foo foo( "hi" ); // OK
  struct Foo( "hi" ); // fail
  Foo foo( "hi" ); // fail
  Foo( "hi" ); // fail
}

इस तरह आप चर का नाम नहीं भूल सकते और आप लिखना नहीं भूल सकते struct। क्रिया, लेकिन सुरक्षित।


1

एक-पैरामीट्रिक निर्माणकर्ता को स्पष्ट घोषित करें और कोई भी कभी भी अनायास ही उस वर्ग की वस्तु नहीं बनाएगा।

उदाहरण के लिए

class Foo
{
public: 
  explicit Foo(const char*);
};

void fun(const Foo&);

केवल इस तरह से इस्तेमाल किया जा सकता है

void g() {
  Foo a("text");
  fun(a);
}

लेकिन कभी इस तरह (स्टैक पर एक अस्थायी के माध्यम से)

void g() {
  fun("text");
}

इन्हें भी देखें: अलेक्जेंड्रेस्कु, सी ++ कोडिंग मानक, आइटम 40।


3
यह अनुमति देता है fun(Foo("text"));
गुइलहर्मे बर्नल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.