C ++ में नेस्टेड प्रकार / वर्गों की फॉरवर्ड घोषणा


197

मैं हाल ही में इस तरह की स्थिति में फंस गया:

class A
{
public:
    typedef struct/class {...} B;
...
    C::D *someField;
}

class C
{
public:
    typedef struct/class {...} D;
...
    A::B *someField;
}

आमतौर पर आप एक वर्ग नाम घोषित कर सकते हैं:

class A;

लेकिन आप आगे किसी नेस्टेड प्रकार की घोषणा नहीं कर सकते, निम्नलिखित कारण संकलन त्रुटि है।

class C::D;

कोई विचार?


6
आपको इसकी आवश्यकता क्यों है? ध्यान दें कि आप घोषणा कर सकते हैं कि क्या यह उसी वर्ग का सदस्य है जिसे परिभाषित किया जा रहा है: कक्षा X {वर्ग Y; य * ए; }; कक्षा X :: Y {};
जोहान्स शाउब -

इस समाधान ने मेरे लिए काम किया (नाम स्थान C {वर्ग D;};): stackoverflow.com/questions/22389784/…
Albert Wiersch

मुझे एक समाधान लिंक
bitlixi

जवाबों:


224

आप ऐसा नहीं कर सकते, यह C ++ भाषा में एक छेद है। आपको कम से कम नेस्टेड कक्षाओं में से एक को घोंसला बनाना होगा।


6
जवाब के लिए धन्यवाद। मेरे मामले में, वे मेरी नेस्टेड कक्षाएं नहीं हैं। मैं थोड़ा आगे के संदर्भ के साथ एक विशाल पुस्तकालय हेडर फ़ाइल निर्भरता से बचने की उम्मीद कर रहा था। मुझे आश्चर्य है कि अगर C ++ 11 इसे तय किया?
मार्श रे

61
ओह। बस क्या मैं नहीं दिखाना चाहता था कि गूगल। संक्षिप्त उत्तर के लिए वैसे भी धन्यवाद।
learnvst

19
यहाँ भी ... किसी को पता है कि यह क्यों संभव नहीं है? ऐसा लगता है कि वैध उपयोग के मामले हैं, और यह कमी कुछ स्थितियों में वास्तुकला की स्थिरता को रोकती है।
माएल नाइसन

आप दोस्त का उपयोग कर सकते हैं। और बस एक टिप्पणी डालें कि आप इसका उपयोग C ++ में छेद के चारों ओर काम करने के लिए कर रहे हैं।
एरिक एरोनिटी

3
जब भी मैं इस ersatz भाषा में इस तरह की अनावश्यक खामियों का सामना करता हूं, तो मैं हंसी और रोने के बीच फट जाता हूं
SongWithoutWords

33
class IDontControl
{
    class Nested
    {
        Nested(int i);
    };
};

मुझे आगे के संदर्भ की आवश्यकता थी जैसे:

class IDontControl::Nested; // But this doesn't work.

मेरा समाधान था:

class IDontControl_Nested; // Forward reference to distinct name.

बाद में जब मैं पूरी परिभाषा का उपयोग कर सकता था:

#include <idontcontrol.h>

// I defined the forward ref like this:
class IDontControl_Nested : public IDontControl::Nested
{
    // Needed to make a forwarding constructor here
    IDontControl_Nested(int i) : Nested(i) { }
};

यदि जटिल कंस्ट्रक्टर या अन्य विशेष सदस्य फ़ंक्शंस थे जो सुचारू रूप से विरासत में नहीं मिले, तो यह तकनीक शायद अधिक परेशानी वाली बात होगी। मैं कल्पना कर सकता हूं कि कुछ टेम्प्लेट जादू बुरी तरह से प्रतिक्रिया कर रहे हैं।

लेकिन मेरे बहुत ही साधारण मामले में, यह काम करने लगता है।


16
C ++ 11 में आप using basename::basename;व्युत्पन्न वर्ग द्वारा कंस्ट्रक्टरों को विरासत में प्राप्त कर सकते हैं , इस प्रकार जटिल सीवर्स के साथ कोई समस्या नहीं है।
Xeo

1
अच्छी चाल, लेकिन यह काम नहीं करेगा अगर सूचक IDontControl :: नेस्टेड एक ही हैडर (जहां यह आगे घोषित किया गया हो) के भीतर उपयोग किया जाता है और बाहरी कोड से एक्सेस किया जाता है जिसमें IDontControl की पूरी परिभाषा भी शामिल है। (क्योंकि कंपाइलर IDontControl_Nested और IDontControl :: Nested से मेल नहीं खाएगा)। स्थैतिक कलाकारों का प्रदर्शन करना है।
आर्टेम पिसरेन्को

मैं विपरीत कर और कक्षा के बाहर होने की सलाह देते हैं, और बस का उपयोग typedefअंदर वर्ग
ridderhoff

3

यदि आप वास्तव में अपनी हेडर फ़ाइल में # नॉटी हेडर फ़ाइल को हटाने से बचना चाहते हैं, तो आप ऐसा कर सकते हैं:

hpp फ़ाइल:

class MyClass
{
public:
    template<typename ThrowAway>
    void doesStuff();
};

cpp फ़ाइल

#include "MyClass.hpp"
#include "Annoying-3rd-party.hpp"

template<> void MyClass::doesStuff<This::Is::An::Embedded::Type>()
{
    // ...
}

परन्तु फिर:

  1. आपको कॉल समय पर एम्बेडेड प्रकार निर्दिष्ट करना होगा (विशेषकर यदि आपका फ़ंक्शन एम्बेडेड प्रकार के किसी भी पैरामीटर को नहीं लेता है)
  2. आपका कार्य आभासी नहीं हो सकता (क्योंकि यह एक टेम्पलेट है)

तो, हाँ, tradeoffs ...


1
क्या बिल्ली एक hppफ़ाइल है?
नफतली उर्फ ​​नील

7
lol, .hpp हेडर फ़ाइल का उपयोग C ++ प्रोजेक्ट्स में किया जाता है ताकि इसे C हेडर फ़ाइल से अलग किया जा सके जो आमतौर पर .h के साथ समाप्त होती है। एक ही प्रोजेक्ट में C ++ और C के साथ काम करते समय कुछ लोग .hpp और .cpp को C ++ फ़ाइलों के लिए पसंद करते हैं, ताकि यह स्पष्ट रूप से हो सके कि किस प्रकार की फाइलें उनके साथ काम कर रही हैं, और .h और .c के लिए सी फाइलें।
bitek

2

यह आगे चलकर बाहरी वर्ग को नेमस्पेस घोषित कर सकता है ।

नमूना: हमें एक नेस्टेड क्लास दूसरों का उपयोग करना है :: ए :: दूसरों में नेस्टेड_एएच, जो हमारे नियंत्रण से बाहर है।

others_a.h

namespace others {
struct A {
    struct Nested {
        Nested(int i) :i(i) {}
        int i{};
        void print() const { std::cout << i << std::endl; }
    };
};
}

my_class.h

#ifndef MY_CLASS_CPP
// A is actually a class
namespace others { namespace A { class Nested; } }
#endif

class MyClass {
public:
    MyClass(int i);
    ~MyClass();
    void print() const;
private:
    std::unique_ptr<others::A::Nested> _aNested;
};

my_class.cpp

#include "others_a.h"
#define MY_CLASS_CPP // Must before include my_class.h
#include "my_class.h"

MyClass::MyClass(int i) :
    _aNested(std::make_unique<others::A::Nested>(i)) {}
MyClass::~MyClass() {}
void MyClass::print() const {
    _aNested->print();
}

1
यह काम कर सकता है, लेकिन यह अनिर्दिष्ट है। कारण यह है कि यह काम a::bउसी तरह से किया जाता है, चाहे aवह वर्ग या नामस्थान ही क्यों न हो।
जस्कमर

3
क्लैंग या जीसीसी के साथ काम नहीं करता है। इसमें कहा गया है कि बाहरी वर्ग को किसी नाम स्थान से अलग घोषित किया गया था।
डुगी

1

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

namespace C {
    typedef struct {} D;
}

class A
{
public:
 typedef struct/class {...} B;
...
C::D *someField;
}

class C
{
public:
   typedef struct/class {...} D;
...
   A::B *someField;
}

1
मैंने साइबरविन जीसीसी के साथ यह कोशिश की और यदि आप ए.सोमफिल्ड को संदर्भित करने का प्रयास करते हैं तो यह संकलन नहीं करता है। C :: D कक्षा में एक परिभाषा वास्तव में नाम स्थान में रिक्त (रिक्त) संरचना को संदर्भित करता है, वर्ग C में संरचना नहीं (BTW यह MSVC में संकलित नहीं करता है)
Dolphin

यह त्रुटि देता है: "'C वर्ग C' को विभिन्न प्रकार के प्रतीक के रूप में
पुन: घोषित किया गया है

9
जीसीसी बग जैसा दिखता है। ऐसा लगता है कि एक नेमस्पेस नाम एक वर्ग के नाम को एक ही दायरे में छिपा सकता है।
जोहान्स शाउब -

0

यह एक वर्कअराउंड होगा (कम से कम प्रश्न में वर्णित समस्या के लिए - वास्तविक समस्या के लिए नहीं, अर्थात, जब परिभाषा पर नियंत्रण न हो C):

class C_base {
public:
    class D { }; // definition of C::D
    // can also just be forward declared, if it needs members of A or A::B
};
class A {
public:
    class B { };
    C_base::D *someField; // need to call it C_base::D here
};
class C : public C_base { // inherits C_base::D
public:
    // Danger: Do not redeclare class D here!!
    // Depending on your compiler flags, you may not even get a warning
    // class D { };
    A::B *someField;
};

int main() {
    A a;
    C::D * test = a.someField; // here it can be called C::D
}

0

यदि आपके पास कक्षा C और D के स्रोत कोड को बदलने की सुविधा है, तो आप कक्षा D को अलग से निकाल सकते हैं, और कक्षा C में इसके लिए एक पर्यायवाची दर्ज कर सकते हैं:

class CD {

};

class C {
public:

    using D = CD;

};

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