2016-03-10 17 views
17

Các mảnh đơn giản sau đây của mã biên dịch, mặc dù tôi không hiểu tại sao:C++ Chuyển Tuyên bố Lớp Trong Lớp

class C { 
    class B; 

    class A { 
     B getB() { return B(); } 
    }; 

    class B { 
    }; 
}; 

int main(int, char**) 
{ 
    return 0; 
} 

Nếu tôi sau đó nhận xét ra các "class C" công cụ, do đó việc kê khai về phía trước của B , định nghĩa về A và định nghĩa của B không còn lồng trong một lớp, mã số không biên dịch, vì B là của một loại không đầy đủ:

main.cpp: In member function 'B A::getB()': 
main.cpp:6: error: return type 'struct B' is incomplete 
main.cpp:6: error: invalid use of incomplete type 'struct B' 
main.cpp:3: error: forward declaration of 'struct B' 

tôi underst và nó có nghĩa là gì đối với một loại không đầy đủ, cụ thể là nó chưa được định nghĩa và do đó trình biên dịch không thể biết bao nhiêu không gian để phân bổ cho nó. Nhưng tại sao là B không được coi là không đầy đủ trong mã ở trên, trong đó AB đều được khai báo và xác định bên trong của C?

Trả lời

10

Tôi tin rằng đây là một hệ quả của [basic.scope.class]:

Phạm vi tiềm năng của một tên tuyên bố trong một lớp học bao gồm không chỉ của khu vực tường thuật sau đây quan điểm tên của tờ khai, mà còn của tất cả các cơ quan chức năng , đối số mặc định, đặc điểm kỹ thuật ngoại lệbộ khởi tạo cú đúp hoặc bằng nhau thành viên dữ liệu không tĩnh trong lớp đó (bao gồm những thứ như vậy trong lồng nhau lớp).

Đó là, phạm vi của việc kê khai đầy đủ của B bao gồm các cơ quan chức năng thành viên của lớp lồng nhau:

class C { 
    class B; // (1) 

    class A { 
     B getB() { 
      return B(); // both (1) and (2) in scope here 
         // since (2) is the complete type declaration, 
         // this is perfectly fine 
     } 
    }; 

    class B { // (2) 
    }; 
}; 

Để so sánh, nếu C là một không gian tên thay vì một lớp , phạm vi của tuyên bố đầy đủ của lớp B sẽ không mở rộng thành A::getB(). Tuyên bố có thể nhìn thấy duy nhất sẽ là khai báo chuyển tiếp của B mà tôi đã gắn nhãn (1) - vì vậy B() sẽ là việc xây dựng một loại không đầy đủ ở đó.

+0

Tuyệt vời, cảm ơn bạn đã dọn dẹp. Câu hỏi cụ thể này là lớp ['result'] (http://pqxx.org/devprojects/libpqxx/doc/stable/html/Reference/) của libpqxx, lớp này có các lớp' tuple' và 'field' lồng nhau, trong 'tuple' xây dựng một' trường' trong một trong các cơ quan chức năng của nó (cụ thể là, dòng 183) – villapx

5

Tiêu chuẩn rõ ràng trong việc bắt buộc rằng nội dung của phương thức được diễn giải sau khi lớp bao quanh nó.

Do đó, tại thời điểm đánh giá nội dung của C::A::getB(), A, BC là tất cả các loại hoàn chỉnh.

+0

Bất kỳ cơ hội bạn có thể biết nơi tôi có thể tìm thấy rằng verbiage? – villapx

+1

cố gắng tìm nó ... thật không may, thuật ngữ "chức năng thành viên" được đề cập 582 lần trong dự thảo tiêu chuẩn năm 2015 PDF ... http://open-std.org/JTC1/SC22/WG21/docs/papers/2015 /n4527.pdf –

9

Nội dung của hàm thành viên nội tuyến sẽ không được xử lý cho đến khi định nghĩa lớp đã được xử lý hoàn toàn.

Do đó, bạn có thể sử dụng:

class A 
{ 
    class B; 
    B getB() { return B(); } 

    class B {}; 
}; 

đó cũng cho phép các biến thành viên đó chưa được công bố sẽ được sử dụng trong nội tuyến định nghĩa hàm thành viên.

class Foo 
{ 
    int getBar() { return bar; } 

    int bar; 
}; 

Tôi đoán cùng một logic được mở rộng thành định nghĩa nội tuyến của hàm thành viên của các lớp lồng nhau - tức là chúng không được xử lý cho đến khi định nghĩa lớp chứa được xử lý hoàn toàn.

PS Tôi không thể định vị nhanh tham chiếu trong tiêu chuẩn xác minh xác nhận quyền sở hữu của tôi.

PS 2The answer by Barry có tham chiếu trong tiêu chuẩn làm cho mã trong câu hỏi hợp lệ.

+0

Bạn đánh tôi với câu trả lời. Tôi đã cố gắng tìm những người đứng đầu. Tôi nghĩ rằng nó được hội tụ bởi * Tương tự khi tra cứu tên, khi một id không đủ tiêu chuẩn (5.1) được sử dụng trong định nghĩa một hàm thành viên cho lớp X giải quyết thành một thành viên tĩnh, một điều tra viên hoặc kiểu lồng nhau của lớp X hoặc của một lớp cơ sở của X, id không đủ tiêu chuẩn được chuyển thành một id đủ điều kiện (5.1) trong đó tên-lồng nhau-specifier tên lớp của hàm thành viên. * From ** [class.mfct.non-static] **. Nếu bạn đồng ý hãy tiếp tục và sử dụng nó. – NathanOliver

+0

@NathanOliver Tôi không nghĩ điều đó đúng. Điều đó chỉ nói rằng 'B' sẽ được tìm thấy - nhưng' B' sẽ được tìm thấy bất kể do tuyên bố về phía trước. Tôi không biết phần bên phải là đâu, cũng không thể tìm thấy nó. – Barry

+0

Có thể * Nếu lớp X được định nghĩa trong phạm vi không gian tên, một lớp lồng nhau Y có thể được khai báo trong lớp X và sau đó được định nghĩa trong định nghĩa của lớp X hoặc sau đó được định nghĩa trong phạm vi không gian tên kèm theo định nghĩa lớp X. * ** [class.nest] ** – NathanOliver

0

Bên cạnh đó khi tôi đã có những nhu cầu để chuyển tiếp tuyên bố lớp lồng nhau tôi có xu hướng để ngửi một số thiết kế xấu trong mã của tôi, lừa tôi sử dụng là:

// Foo.h 
class Foo { 
    class Bar { 
    }; 
}; 
class Foobar : public Foo::Bar {}; 


// Zoo.h 
/* Fwd declare */ 
class FooBar; 
+0

Bạn có thể chỉnh sửa câu trả lời của mình – villapx

Các vấn đề liên quan