C ++ 'da iç içe tiplerin / sınıfların ileri bildirimi


197

Son zamanlarda böyle bir durumda takıldım:

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

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

Genellikle bir sınıf adı bildirebilirsiniz:

class A;

Ancak iç içe bir tür bildiremezsiniz, aşağıdakiler derleme hatasına neden olur.

class C::D;

Herhangi bir fikir?


6
Neden buna ihtiyacın var? Tanımlanan aynı sınıfın bir üyesi olup olmadığını ileri sürdüğünüzü not edebilirsiniz: class X {class Y; Y * a; }; sınıf X :: Y {};
Johannes Schaub - litb

Bu çözüm benim için çalıştı (ad alanı C {sınıf D;};): stackoverflow.com/questions/22389784/…
Albert Wiersch

Bir çözüm bağlantısı
bitlixi

Yanıtlar:


224

Bunu yapamazsınız, C ++ dilinde bir delik. Yuvalanmış sınıflardan en az birini yuvalamalısınız.


6
Cevap için teşekkürler. Benim durumumda, bunlar benim iç içe derslerim değil. Biraz ileri referans ile büyük bir kütüphane başlık dosya bağımlılığı önlemek umuyordum. Acaba C ++ 11 düzeltti mi?
Marsh Ray

61
Ah. Google'ın görünmesini istemediğim şey. Özlü cevap için yine de teşekkürler.
learnvst

19
Aynı burada ... birisi neden mümkün olmadığını biliyor mu? Geçerli kullanım durumları var gibi görünüyor ve bu eksiklik bazı durumlarda mimari tutarlılığı önlüyor.
Maël Nison

Arkadaşınızı kullanabilirsiniz. Ve sadece bir açıklama koymak C ++ delik etrafında çalışmak için kullanın.
Erik Aronesty

3
Bu ersatz dilinde böyle gereksiz kusurlarla karşılaştığımda, gülmek ve ağlamak arasında parçalandım
SongWithoutWords

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

Ben gibi ileri bir referans gerekiyordu:

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

Geçici çözümüm:

class IDontControl_Nested; // Forward reference to distinct name.

Daha sonra tam tanımı kullanabileceğim zaman:

#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) { }
};

Bu teknik, sorunsuz bir şekilde miras alınmayan karmaşık kurucular veya diğer özel üye işlevleri olsaydı muhtemelen değerinden daha fazla sorun olurdu. Belli bir şablon büyünün kötü tepki verdiğini hayal edebiliyordum.

Ama benim çok basit durumumda, işe yarıyor gibi görünüyor.


16
C ++ 11'de using basename::basename;türevleri türetilmiş sınıftan miras alabilirsiniz , bu nedenle karmaşık faktörlerle ilgili bir sorun yoktur.
Xeo

1
Güzel hüner, ancak IDontControl :: Nested'e işaretçi aynı üstbilgide (ileri doğru bildirildiği yerde) kullanılırsa ve IDontControl'ün tam tanımını da içeren harici koddan erişilirse çalışmaz. (Derleyici IDontControl_Nested ve IDontControl :: Nested ile eşleşmeyeceğinden). Çözüm, statik yayın gerçekleştirmektir.
Artem Pisarenko

Tam tersini yapmayı ve dışarıda sınıfa sahip olmayı tavsiye ederim ve sadece typedefsınıfın içinde kullan
ridderhoff

3

Gerçekten # başlık dosyanızdaki kötü başlık dosyasını dahil etmekten kaçınmak istiyorsanız, bunu yapabilirsiniz:

hpp dosyası:

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

cpp dosyası

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

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

Ama sonra:

  1. çağrı sırasında gömülü türü belirtmeniz gerekir (özellikle işleviniz gömülü türden herhangi bir parametre almıyorsa)
  2. işleviniz sanal olamaz (bir şablon olduğu için)

Yani, evet, ödünç verme ...


1
Bir halt nedir hppdosya?
Naftali aka Neal

7
lol, bir .hpp başlık dosyası, genellikle .h ile biten bir C başlık dosyasından ayırt etmek için C ++ projelerinde kullanılır. Aynı projede C ++ ve C ile çalışırken, bazı insanlar C ++ dosyaları için .hpp ve .cpp tercih ederler, açıkça hangi dosya türleriyle uğraştıklarını ve C dosyaları için .h ve .c'yi tercih ederler.
bitek

2

Bu , dış sınıfı ad alanı olarak ileri bildirerek yapılabilir .

Örnek: Bizim iç içe bir sınıfı kullanmalıyız: Diğerleri :: A :: Bizim içimizde olan diğer_a.h içinde iç içe.

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
Çalışabilir, ancak belgesizdir. Çalışmasının nedeni , sınıf veya ad alanı a::bne olursa olsun aynı şekilde yönetilmesidir a.
jaskmar

3
Clang veya GCC ile çalışmaz. Dış sınıfın bir ad alanından farklı bir şey olarak bildirildiğini söylüyor.
Dugi

1

Buna bir cevap demezdim, ama yine de ilginç bir bulgu: Yapınızın deklarasyonunu C adlı bir ad alanında tekrarlarsanız, her şey yolunda (en azından gcc olarak). C'nin sınıf tanımı bulunduğunda, sessizce C adının üzerine yazılıyor gibi görünüyor.

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
Bunu cygwin gcc ile denedim ve A.someField'a başvurmaya çalıştığınızda derlemiyor. Sınıf A tanımındaki C :: D aslında isim alanındaki (boş) yapıyı ifade eder, C sınıfındaki yapıyı değil (BTV bu MSVC'de derlenmez)
Dolphin

Hata veriyor: "'C sınıfı" farklı türden sembol olarak yeniden tanımlandı "
Calmarius

9
Bir GCC hatası gibi görünüyor. Bir ad alanı adının bir sınıf adını aynı kapsamda gizleyebileceği düşünülmektedir.
Johannes Schaub - litb

0

Bu bir çözüm olacaktır (en azından soruda açıklanan sorun için - asıl sorun için değil, yani tanımı üzerinde kontrol sahibi olmadığında 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 ve D sınıflarının kaynak kodunu değiştirme erişiminiz varsa, D sınıfını ayrı olarak alabilir ve C sınıfında bunun eşanlamlısını girebilirsiniz:

class CD {

};

class C {
public:

    using D = CD;

};

class CD;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.