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
왜 그런가요? 정의 된 동일한 클래스의 멤버 인 경우 전달할 수 있습니다. class X {class Y; Y * a; }; 클래스 X :: Y {};
Johannes Schaub-litb

나를 위해 일한이 솔루션 (네임 스페이스 C {클래스 D}) : stackoverflow.com/questions/22389784/...
알버트 Wiersch

솔루션 링크를
bitlixi

답변:


224

당신은 그것을 할 수 없습니다, 그것은 C ++ 언어의 구멍입니다. 중첩 클래스 중 하나 이상을 제거해야합니다.


6
답변 해주셔서 감사합니다. 내 경우에는 내 중첩 클래스가 아닙니다. 나는 약간의 앞으로 참조로 거대한 라이브러리 헤더 파일 종속성을 피하기를 바랐습니다. C ++ 11이 수정했는지 궁금합니다.
Marsh Ray

61
오. 내가 Google에 표시하고 싶지 않은 것. 간결한 답변에 감사드립니다.
learnvst

19
여기에서도 마찬가지입니다 ... 누군가 그것이 불가능 한지 알고 있습니까? 유효한 사용 사례가있는 것으로 보이며,이 부족으로 인해 일부 상황에서 아키텍처 일관성이 방지됩니다.
Maël Nison

친구를 사용할 수 있습니다. 그리고 C ++의 구멍을 해결하기 위해 주석을 사용하고 있다는 의견을 넣으십시오.
Erik Aronesty

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;에서는 파생 클래스에서 생성자를 상속 할 수 있으므로 복잡한 ctor에 문제가 없습니다.
Xeo

1
좋은 트릭이지만 IDontControl :: Nested에 대한 포인터가 동일한 헤더 (앞으로 선언 된 위치) 내에서 사용되고 IDontControl의 전체 정의가 포함 된 외부 코드에서 액세스하면 작동하지 않습니다. (컴파일러는 IDontControl_Nested 및 IDontControl :: Nested와 일치하지 않기 때문에). 해결 방법은 정적 캐스트를 수행하는 것입니다.
Artem Pisarenko

나는 반대로 수업을하고 외부에서 수업을받는 것이 좋습니다. 그리고 수업 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. 함수는 템플릿 일 수 있으므로 가상 일 수 없습니다.

예, 트레이드 오프는 ...


1
도대체 hpp파일 은 무엇입니까 ?
Naftali 일명 Neal

7
lol, .hpp 헤더 파일은 C ++ 프로젝트에서 일반적으로 .h로 끝나는 C 헤더 파일과 구별하기 위해 사용됩니다. 동일한 프로젝트에서 C ++ 및 C로 작업 할 때 일부 사람들은 C ++ 파일의 경우 .hpp 및 .cpp를 선호하여 처리하는 파일 형식 및 C 파일의 경우 .h 및 .c를 명시 적으로 지정합니다.
bitek December

2

이것은 외부 클래스를 네임 스페이스로 선언하여 수행 할 수 있습니다 .

샘플 : 우리는 others :: A :: Nested others_a.h에 중첩 된 클래스를 사용해야합니다.

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입니다.
jaskmar

3
Clang 또는 GCC에서는 작동하지 않습니다. 외부 클래스는 네임 스페이스와 다른 것으로 선언되었다고 말합니다.
Dugi

1

나는 이것을 대답이라고 부르지는 않지만 그럼에도 불구하고 흥미로운 발견 : C라는 네임 스페이스에서 구조체 선언을 반복하면 모든 것이 괜찮습니다 (최소한 gcc). C의 클래스 정의가 발견되면 이름 공간 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
cygwin gcc로 이것을 시도했지만 A.someField를 참조하려고하면 컴파일되지 않습니다. 클래스 A 정의의 C :: D는 실제로 클래스 C의 구조체가 아니라 네임 스페이스의 (빈) 구조체를 참조합니다 (BTW MSVC에서 컴파일되지 않음)
Dolphin

" '클래스 C'는 다른 종류의 기호로 다시 선언되었습니다"
Calmarius

9
GCC 버그 인 것 같습니다. 네임 스페이스 이름이 동일한 범위에서 클래스 이름을 숨길 수 있다고 생각합니다.
Johannes Schaub-litb

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;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.