2012-01-28 46 views
10
void foo(int) 
{ 
} 

class X 
{ 
    void foo() 
    { 
    } 

    void bar() 
    { 
     foo(42); 
     // error: no matching function for call to 'X::foo(int)' 
     // note: candidate is: 
     // note: void X::foo() 
     // note: candidate expects 0 arguments, 1 provided   
    } 
}; 

Tại sao C++ không thể gọi hàm miễn phí (đó là hàm duy nhất có chữ ký chính xác)?chức năng thành viên ẩn chức năng miễn phí

+2

Trong trường hợp này, bạn có thể sử dụng ':: foo (42)' để truy cập foo bên ngoài. [Bản trình diễn Ideone] (http://ideone.com/6HljO). Nhưng tôi không biết nhiều về không gian tên. –

+0

Tôi coi đây là một gót chân Achilles của C++. Nó làm cho việc sử dụng các tên hàm miễn phí quá tải phổ biến không thể, chẳng hạn như isempty (điều), nơi có quá nhiều sự quá tải đối với các loại thứ isempty, trong khi cho phép một thing.isempty() tồn tại. Ngu ngốc, không may, vụng về. – Mordachai

Trả lời

5

Lý do logic là quán.

  • Giả sử theo đề nghị, trình biên dịch giải quyết foo(42) để ::foo(int).
  • Bây giờ sau một thời gian, nếu bạn thay đổi X::foo() thành X::foo(int) thì foo(42) sẽ được giải quyết thành X::foo(int). Cái nào không nhất quán.

Đó cũng là lý do tại sao hàm lớp dẫn xuất ẩn chức năng lớp cơ sở khi có tên tương tự.

Các trường hợp như vậy có thể được giải quyết theo 2 cách;

(1) Đưa tên đầy đủ (ví dụ ::foo(42))

(2) Sử dụng using tiện ích; ví dụ.

void bar() 
{ 
    using ::foo; 
    foo(42); 
} 
+1

Nếu một người nào đó sau đó thêm thành viên foo (int), thì họ có ý định rõ ràng điều này. Thiết kế ngôn ngữ xấu, IMO. – Mordachai

12

Vì hai số nhận dạng được xác định trong các phạm vi khác nhau và độ phân giải quá tải chỉ quan tâm đến các chức năng trong cùng phạm vi. Một khi trình biên dịch phát hiện ra rằng lớp đó có một số foo, nó ngừng leo lên đến phạm vi rộng hơn (C++ 11 §3.4.1/1), do đó, chức năng miễn phí foo bị ẩn.

Bạn cần phải sử dụng một tên đủ điều kiện để tham khảo cho toàn cầu foo:

::foo(42); 
+1

Lưu ý: đây là một trường hợp cụ thể vì 'int', hầu hết các lần nó vẫn hoạt động vì ADL là tốt. –

0

tôi không thể trả lời câu hỏi tại sao một phần của câu hỏi của bạn - Tôi không biết lý do đằng sau đó trong spec ngôn ngữ là gì .

Để gọi hàm toàn cầu trong ví dụ của bạn, sử dụng :: cú pháp:

::foo(42); 
0

Lý do thực tế là trình biên dịch sẽ tìm kiếm tên hàm phù hợp trước tiên, bỏ qua giá trị trả về và tham số. Khi bên trong một lớp, nó sẽ cố gắng tìm một thành viên phù hợp ở đó (trên thực tế, nó sẽ xem xét tất cả các phạm vi "lên trên"; phạm vi cục bộ, phạm vi chức năng, phạm vi lớp, phạm vi không gian tên, phạm vi toàn cầu, v.v.).

X::foo là tên phù hợp đầu tiên. THEN (không phải trước đó) nó sẽ cố gắng chọn đúng tình trạng quá tải (nếu có nhiều khai báo) dựa trên các tham số (đó là lý do bạn có thể quá tải cùng chức năng với các tham số khác nhau nhưng không chỉ giá trị trả về khác nhau) và sau đó nó sẽ kiểm tra giá trị trả về (nếu có).

1

Thực sự thích câu hỏi của bạn.Ngoài ra tôi có thể nói, sử dụng cú pháp sau:

::foo(42); 

Nhưng tôi có thể nói rằng theo ý kiến ​​của tôi nó hơn tao nhã và tốt lập trình, thiết lập không gian tên, vì vậy bạn có thể viết một cái gì đó như thế này:

namespace MyNameSpace 
{ 
    void foo(int){} 

    class X 
    { 
     void foo(){} 

     void bar() 
     { 
      MyNameSpace::foo(42); 
     } 
    }; 
}; 

này là một điều tốt bởi vì Namespaces cho phép nhóm các lớp, các đối tượng và chức năng dưới một tên.

PS: Sau đó, điều này giúp bạn hiểu ý nghĩa của việc viết ::foo(42); khi bạn không có bất kỳ không gian tên nào.

2

Tên trong phạm vi bên trong sẽ ẩn tên trong phạm vi bên ngoài. Nó không quan trọng nếu nó là một chức năng hay cái gì khác, hoặc nếu bạn đang ở trong một lớp hoặc một không gian tên.

Chỉ khi tra cứu tên tìm thấy một số chức năng có cùng tên, thì độ phân giải quá tải sẽ bắt đầu để cố gắng chọn một kết quả phù hợp nhất cho cuộc gọi.

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