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.