2012-11-23 27 views
118

tôi đã bằng cách nào đó ngạc nhiên rằng đoạn mã sau biên dịch và chạy (vc2012 & gcc4.7.2)Tại sao tôi có thể sử dụng tự động trên một loại riêng tư?

class Foo { 
    struct Bar { int i; }; 
public: 
    Bar Baz() { return Bar(); } 
}; 

int main() { 
    Foo f; 
    // Foo::Bar b = f.Baz(); // error 
    auto b = f.Baz();   // ok 
    std::cout << b.i; 
} 

Có đúng là mã này biên dịch tốt? Và tại sao nó lại đúng? Tại sao tôi có thể sử dụng auto trên một loại riêng tư, trong khi tôi không thể sử dụng tên của nó (như mong đợi)?

+10

Quan sát rằng 'f.Baz i'() cũng là OK, như là 'std :: cout << typeid (f.Baz()). name() '. Mã bên ngoài lớp có thể "nhìn thấy" kiểu được trả về bởi 'Baz()' nếu bạn có thể giữ nó, bạn không thể đặt tên nó. –

+2

Và nếu bạn nghĩ nó kỳ quặc (bạn có thể làm như vậy, nhìn thấy khi bạn đang hỏi về nó) bạn không phải là người duy nhất;) Chiến lược này rất hữu ích cho những thứ như [Safe-Bool Idiom] (http: // www .artima.com/cppsource/safebool.html). –

+2

Tôi nghĩ rằng điều cần nhớ là 'private' là có một sự thuận tiện cho việc mô tả các API theo cách mà trình biên dịch có thể giúp thực thi. Nó không có ý định ngăn chặn truy cập vào kiểu 'Bar' bởi người dùng' Foo', vì vậy nó không cản trở 'Foo' bằng cách cung cấp truy cập đó bằng cách trả về một thể hiện của' Bar'. –

Trả lời

100

Các quy tắc cho auto, đối với hầu hết các phần, giống như đối với loại khấu trừ mẫu. Ví dụ được đăng hoạt động cho cùng một lý do bạn có thể chuyển đối tượng của các loại riêng tư cho các chức năng mẫu:

template <typename T> 
void fun(T t) {} 

int main() { 
    Foo f; 
    fun(f.Baz());   // ok 
} 

Và tại sao chúng ta có thể chuyển đối tượng của các loại riêng sang chức năng mẫu? Bởi vì chỉ có tên của loại là không thể tiếp cận. Bản thân loại này vẫn có thể sử dụng được, đó là lý do tại sao bạn có thể trả lại nó cho mã máy khách.

+25

Và để thấy rằng tính riêng tư của tên * không liên quan gì đến * type *, hãy thêm 'public: typedef Bar return_type_from_Baz;' vào lớp 'Foo' trong câu hỏi. Bây giờ loại có thể được xác định bởi một tên công cộng, mặc dù được định nghĩa trong một phần riêng tư của lớp. –

+1

Để lặp lại điểm của @ Steve: thông số truy cập cho _name_ không liên quan gì đến _type_, như được thấy bằng cách thêm 'private: typedef Bar return_type_from_Baz;' thành 'Foo', như [đã trình bày] (http://ideone.com/iKv2bQ). Các định danh 'typedef''d là không thể truy cập các specifier, public và private. – damienh

+0

Điều này không có ý nghĩa với tôi. Các _name_ của loại chỉ đơn thuần là một bí danh cho các loại thực tế. Có vấn đề gì nếu tôi gọi nó là 'Bar' hoặc' SomeDeducedType'? Nó không giống như tôi có thể sử dụng nó để có được các thành viên tư nhân của 'lớp Foo' hay bất cứ điều gì. – einpoklum

98

Kiểm soát truy cập được áp dụng cho tên. So sánh với ví dụ này từ tiêu chuẩn:

class A { 
    class B { }; 
public: 
    typedef B BB; 
}; 

void f() { 
    A::BB x; // OK, typedef name A::BB is public 
    A::B y; // access error, A::B is private 
} 
5

Để thêm vào các câu trả lời (tốt) khác, đây là một ví dụ từ C++ 98 minh họa rằng vấn đề thực sự không phải làm với auto ở tất cả

class Foo { 
    struct Bar { int i; }; 
public: 
    Bar Baz() { return Bar(); } 
    void Qaz(Bar) {} 
}; 

int main() { 
    Foo f; 
    f.Qaz(f.Baz()); // Ok 
    // Foo::Bar x = f.Baz(); 
    // f.Qaz(x); 
    // Error: error: ‘struct Foo::Bar’ is private 
} 

Sử dụng loại riêng tư không bị cấm, nó chỉ đặt tên loại. Việc tạo một tạm thời chưa được đặt tên của kiểu đó là không sao, ví dụ, trong tất cả các phiên bản của C++.

8

Câu hỏi này đã được trả lời rất tốt bởi cả lạnh và R. Martinho Fernandes.

Tôi chỉ không thể bỏ qua cơ hội này để trả lời một câu hỏi với một loại suy Harry Potter:.

class Wizard 
{ 
private: 
    class LordVoldemort 
    { 
     void avada_kedavra() 
     { 
      // scary stuff 
     } 
    }; 
public: 
    using HeWhoMustNotBeNamed = LordVoldemort; 
}; 

int main() 
{ 
    Wizard::HeWhoMustNotBeNamed tom; // OK 
    Wizard::LordVoldemort not_allowed; // Not OK 
    return 0; 
} 
+4

Không phải là một 'người bạn lớp Harry;' thiếu trong đó? – Quentin

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