2010-11-11 41 views
7

xem xét:Không ghi đè một phương pháp ảo không phải là const ẩn một quá tải const?

#include <iostream> 

using namespace std; 

struct A { 
    virtual void f() { cout << "A::f" << endl; } 
    virtual void f() const { cout << "A::f const" << endl; } 
}; 

struct B : public A {}; 

struct C : public A { 
    virtual void f() { cout << "C::f" << endl; } 
}; 


int main() 
{ 
    const B b; 
    b.f(); // prints "A::f const" 

    const C c; 
    c.f(); 
    // Compile-time error: passing ‘const C’ as ‘this’ argument of 
    // ‘virtual void C::f()’ discards qualifiers 
} 

(. Tôi đang sử dụng GCC)

Vì vậy, có vẻ như phiên bản const của f() được ẩn trong C. Điều này làm cho rất nhiều ý nghĩa đối với tôi, nhưng là nó bắt buộc theo tiêu chuẩn?

+4

"Ảo" là cá trích đỏ. Chúng tôi không gọi bất kỳ 'f' hầu như (thông qua một con trỏ lớp cơ sở hoặc tài liệu tham khảo) ở đây. Tất cả các tra cứu của 'f' tìm ra' f' có nguồn gốc nhiều nhất. – MSalters

+0

Ảo và const không thực sự áp dụng cho câu hỏi, nhưng tôi đã để chúng dưới dạng thẻ vì tôi không thấy nhiều tác hại và không cần phải bao gồm thẻ có liên quan hơn. –

+2

Tôi đồng ý về 'virtual', nhưng' const' là toàn bộ câu hỏi. Ghi đè 'f()' ẩn 'f() const'. – Ari

Trả lời

4

tôi sẽ (một lần nữa) liên kết này lớn article:

Thứ nhất, [trình biên dịch] trông trong phạm vi trước mắt , trong trường hợp này phạm vi của lớp C, và làm cho một danh sách các tất cả các chức năng mà nó có thể tìm thấy là có tên f (bất kể chúng có phải là có thể truy cập hoặc thậm chí lấy số lượng thông số đúng). Chỉ khi nó không nó sau đó tiếp tục "bên ngoài" vào hôm sau, kèm theo phạm vi [...]

Vì vậy, có, phiên bản const của f được ẩn, và đó là hoàn toàn bình thường. Như được chỉ ra bởi Simone, bạn có thể sử dụng câu lệnh using để mang lại A::f trong phạm vi C.

+0

+1 Bài viết hay. Như một điểm đáng chú ý, điều này cũng được thảo luận trong mục 33 của C++ phiên bản thứ ba hiệu quả –

2

Chèn using B::f;

struct C : public A { 
    using A::f; 
    virtual void f() { cout << "C::f" << endl; } 
}; 

C++ chuẩn năm 2003. 13,2 p.1:

Hai tờ khai chức năng cùng tên tham khảo với cùng chức năng nếu họ đang ở trong phạm vi tương tự và có khai báo tham số tương đương (13.1). Một hàm thành viên của lớp dẫn xuất không phải là trong cùng một phạm vi với tư cách là thành viên chức năng cùng tên trong một lớp cơ sở.

Vì vậy, C::f ẩn tất cả A::f.

+2

Thid không biên dịch. Có thể bạn có nghĩa là "A :: f". – Simone

+0

Xin lỗi. Tất nhiên, A :: f. –

3

Vâng, đúng vậy. Bạn có thể viết:

struct C : public A { 
    virtual void f() { cout << "C::f" << endl; } 
    using A::f;  
}; 

để làm cho mã biên dịch của bạn:

int main() 
{ 
    const B b; 
    b.f(); // prints "A::f const" 

    const C c; 
    c.f(); // prints "A::f const" 
} 

Để biết thêm infos, bạn có thể tham khảo 2010 C++ Dự thảo tài liệu (mà bạn có thể tìm here) chương 10.2 (3. 4).

+0

+1 để tham chiếu đến tiêu chuẩn, nhưng tôi có thêm thông tin từ câu trả lời của icecrime. Tôi đã nhận thức rõ về tùy chọn 'using'. Tôi không quan tâm đến việc biên dịch, nhưng đúng hơn là hiểu ngôn ngữ. – Ari

+0

Cảm ơn bạn Ari, nhưng hãy nhớ rằng câu hỏi của bạn có thể được tư vấn bởi những người có cùng một vấn đề, những người cũng muốn làm cho nó biên dịch. Tốt hơn là viết một cái gì đó nhiều hơn một cái gì đó ít hơn, bạn có đồng ý không? :) – Simone

3

Nó không phải là ảo hoặc const-ness (hoặc thiếu) mà ẩn các thành viên cơ sở, bất kỳ phương pháp có nguồn gốc ẩn một phương pháp cơ sở cùng tên. Điều này đã được thực hiện để cải thiện vấn đề lớp cơ sở mong manh.

Imagine mã của bạn đang làm việc (có thể trong nhiều năm qua) như dưới đây, với các bộ phận không liên quan loại bỏ:

struct Base { 
}; 

struct Derived : Base { 
    void f(double); 
} 

void g(Derived &d) { 
    d.f(42); 
} 

Sau đó, bạn cần phải sửa đổi cơ sở để bao gồm một phương pháp mà làm điều gì đó hoàn toàn khác nhau, nhưng, đối với một số lý do, bạn muốn đặt tên nó 'f':

struct Base { 
    void f(int); 
}; 

nếu không có quy tắc này, mỗi sử dụng một nguồn gốc gọi f cần phải được đánh giá bằng tay - và nếu Base là trong một thư viện dành cho người khác, bạn thậm chí không thể có quyền truy cập vào các mục đích sử dụng khác đó! Nó trở nên tồi tệ hơn khi đối mặt với các chuyển đổi do người dùng xác định (ẩn). Thay vào đó, nó đã được quyết định yêu cầu các lớp dẫn xuất để nói rõ rằng họ muốn nhập các tên đã cho từ Base với một khai báo sử dụng. Quy tắc này có thể gây ngạc nhiên và tôi không chắc chắn đó là một lợi ích ròng cho ngôn ngữ ngày hôm nay, nhưng họ không hỏi tôi - vào lúc đó, tôi có lẽ chỉ có thể trả lời chúng bằng hai từ. :)

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