2009-06-04 34 views
164

Gần đây tôi đã bị mắc kẹt trong một tình huống như thế này:Chuyển tiếp tuyên bố của các loại/lớp lồng nhau trong C++

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

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

Thông thường, bạn có thể khai báo một tên lớp:

class A; 

Nhưng bạn không thể mong khai báo một kiểu lồng nhau, sau đây gây ra lỗi biên dịch.

class C::D; 

Bất kỳ ý tưởng nào?

+5

Tại sao bạn cần điều đó? Lưu ý rằng bạn có thể chuyển tiếp khai báo nếu nó là một thành viên của cùng một lớp đang được định nghĩa: class X {class Y; Y * a; }; lớp X :: Y {}; –

+1

Lỗi hấp dẫn. –

+0

Giải pháp này làm việc cho tôi (không gian tên C {class D;};): http://stackoverflow.com/questions/22389784/c-code-fails-to-compile-after-upgrading-xcode-5-0-5 -1-forward-declaration –

Trả lời

179

Bạn không thể làm điều đó, đó là một lỗ hổng trong ngôn ngữ C++. Bạn sẽ phải hủy bỏ ít nhất một trong các lớp lồng nhau.

+5

Cảm ơn câu trả lời. Trong trường hợp của tôi, chúng không phải là lớp lồng nhau của tôi. Tôi đã hy vọng để tránh một phụ thuộc tập tin thư viện lớn phụ thuộc với một chút tham khảo về phía trước. Tôi tự hỏi nếu C++ 11 sửa nó? –

+46

Ồ. Chỉ là những gì tôi không muốn google hiển thị. Cảm ơn anyway cho câu trả lời ngắn gọn. – learnvst

+15

Tương tự ở đây ... không ai biết * tại sao * không thể? Dường như có trường hợp sử dụng hợp lệ và sự thiếu này ngăn cản sự nhất quán về kiến ​​trúc trong một số trường hợp. –

0

Tôi sẽ không gọi đây là câu trả lời, nhưng dù sao tìm kiếm thú vị: Nếu bạn lặp lại khai báo cấu trúc của bạn trong một không gian tên gọi là C, mọi thứ đều ổn (ít nhất là gcc). Khi định nghĩa lớp của C được tìm thấy, nó có vẻ âm thầm ghi đè lên C. namspace

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

Tôi đã thử điều này với Cygwin gcc và nó không biên dịch nếu bạn cố gắng tham khảo A.someField. C :: D trong lớp Một định nghĩa thực sự đề cập đến cấu trúc (rỗng) trong không gian tên, không phải cấu trúc trong lớp C (BTW này không biên dịch trong MSVC) – Dolphin

+0

Nó cung cấp lỗi: "lớp C" 'redeclared như là loại khác nhau của biểu tượng " – Calmarius

+7

Hình như một lỗi GCC. Dường như nghĩ rằng một tên không gian tên có thể ẩn một tên lớp trong cùng một phạm vi. –

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

tôi cần một tham chiếu về phía trước như:

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

workaround của tôi là:

class IDontControl_Nested; // Forward reference to distinct name. 

Sau đó khi tôi có thể sử dụng định nghĩa đầy đủ:

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

Kỹ thuật này có thể sẽ gặp nhiều rắc rối hơn giá trị nếu có các hàm tạo phức tạp hoặc các hàm thành viên đặc biệt khác không được kế thừa thuận lợi. Tôi có thể tưởng tượng một số ma thuật mẫu nhất định phản ứng nặng nề.

Nhưng trong trường hợp rất đơn giản của tôi, có vẻ như nó hoạt động.

+10

Trong C++ 11, bạn có thể kế thừa các hàm tạo bởi 'using basename :: basename;' trong lớp dẫn xuất, do đó không có vấn đề gì với các ctors phức tạp. – Xeo

+1

Thủ thuật đẹp, nhưng nó sẽ không hoạt động nếu con trỏ đến IDontControl :: Lồng nhau được sử dụng trong cùng một tiêu đề (nơi nó được khai báo trước) và truy cập từ mã bên ngoài cũng bao gồm định nghĩa đầy đủ của IDontControl. (Bởi vì trình biên dịch sẽ không khớp với IDontControl_Nested và IDontControl :: Nested). Giải pháp thay thế là thực hiện truyền tĩnh. –

3

Nếu bạn thực sự muốn tránh #including file header khó chịu trong tập tin tiêu đề của bạn, bạn có thể làm điều này:

hpp file:

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

tập tin cpp

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

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

Nhưng sau đó:

  1. bạn sẽ phải xác định loại nhúng vào thời điểm cuộc gọi (đặc biệt nếu chức năng của bạn không mất bất kỳ thông số của các loại nhúng)
  2. chức năng của bạn không thể ảo (vì nó là một mẫu)

Vì vậy, vâng, sự cân bằng ...

+1

Heck là gì? – Neal

+6

lol, một tệp tiêu đề **. Hpp ** được sử dụng trong các dự án C++ để phân biệt nó với tệp tiêu đề C thường kết thúc bằng .h. Khi làm việc với C++ và C trong cùng một dự án, một số người thích tệp .hpp và .cpp cho tệp C++, để làm cho nó rõ ràng với loại tệp mà họ xử lý, và .h và .c cho tệp C. – bitek

0

Đây sẽ là một giải pháp (ít nhất là cho vấn đề được mô tả trong câu hỏi - không phải cho vấn đề thực tế, tức là, khi không có quyền kiểm soát định nghĩa của 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 
} 
Các vấn đề liên quan