2011-10-16 18 views
39

Có mã như:Tiếp cận người bạn hàm được định nghĩa trong lớp

#include <iostream> 

class A{ 

public: 
    friend void fun(A a){std::cout << "Im here" << std::endl;} 
    friend void fun2(){ std::cout << "Im here2" << std::endl; } 
    friend void fun3(); 
}; 

void fun3(){ 
    std::cout << "Im here3" << std::endl; 
} 

int main() 
{ 
    fun(A()); // works ok 
    //fun2(); error: 'fun2' was not declared in this scope 
    //A::fun2(); error: 'fun2' is not a member of 'A' 
    fun3(); // works ok 
} 

Làm thế nào để truy cập vào chức năng fun2()?

+6

+1: Câu hỏi được xây dựng kỹ lưỡng. –

+0

Chúng ta thấy loại mã này trong con trỏ thông minh của boost: intrusive_ptr, điều này làm cho tôi bắt đầu câu đố đầu tiên. Đối với tôi nó không có ý nghĩa để xác định nó theo cách đó, thay vì xác định một người bạn ở đâu đó trong phạm vi và đánh dấu một nguyên mẫu làm bạn trong khai báo phạm vi lớp, đó là dễ đọc hơn! – Gabriel

Trả lời

34
class A{ 

public: 
    friend void fun(A a){std::cout << "Im here" << std::endl;} 
    friend void fun2(){ std::cout << "Im here2" << std::endl; } 
    friend void fun3(); 
}; 

Mặc dù định nghĩa của bạn về fun2không định nghĩa một hàm "toàn cầu" chứ không phải là thành viên, và làm cho nó một friend của A cùng một lúc, bạn vẫn thiếu một tuyên bố chức năng tương tự trong phạm vi toàn cầu.

Điều đó có nghĩa là không có mã nào trong phạm vi đó có bất kỳ ý tưởng nào rằng fun2 tồn tại.

Vấn đề tương tự xảy ra đối với fun, ngoại trừ truy vấn đối tượng phụ thuộc có thể tiếp quản và tìm hàm, vì có đối số thuộc loại A.

Tôi khuyên bạn nên thay vì quy định chức năng của bạn theo cách thông thường:

class A { 
    friend void fun(A a); 
    friend void fun2(); 
    friend void fun3(); 
}; 

void fun(A a) { std::cout << "I'm here" << std::endl; } 
void fun2() { std::cout << "I'm here2" << std::endl; } 
void fun3(); 

Thông báo bây giờ mà everything works (trừ fun3 bởi vì tôi không bao giờ định nghĩa nó).

21

Lý do bạn có thể gọi fun là tuyên bố bạn bè bên trong lớp A chỉ hiển thị thông qua tìm kiếm phụ thuộc đối số. Nếu không, bạn khai báo không làm cho các hàm mà chúng khai báo tự động hiển thị bên ngoài phạm vi lớp nơi xuất hiện.

Bạn cần thêm tuyên bố ở phạm vi không gian tên hoặc bên trong main để hiển thị fun2main.

Ví dụ:

void fun2(); 

fun3 có thể nhìn thấy bên trong main vì định nghĩa của nó (bên ngoài của lớp) cũng là một lời tuyên bố mà làm cho nó có thể nhìn thấy từ main.

ISO/IEC 14882: 2011 7.3.1.2:

Tên của bạn không được tìm thấy bằng cách tra cứu không đủ tiêu chuẩn (3.4.1) hoặc bằng cách tra cứu đủ điều kiện (3.4.3) cho đến khi tuyên bố phù hợp là được cung cấp trong phạm vi không gian tên đó (trước hoặc sau khi định nghĩa lớp cấp tình bạn).

3.4.2 (Đối số phụ thuộc vào tra cứu tên)/4:

Bất kỳ namespace-phạm vi chức năng bạn bè hoặc người mẫu hàm bạn bè khai báo trong lớp học có liên quan có thể nhìn thấy trong không gian tên tương ứng của họ ngay cả khi họ không hiển thị trong quá trình tra cứu thông thường (11.3).

+0

việc đặt khai báo trong không gian tên toàn cầu giải quyết được vấn đề, tuy nhiên việc đặt nó vào chức năng chính sẽ tạo ra lỗi liên kết. cảm ơn. – scdmb

+0

@scdmb: Tôi ngạc nhiên về lỗi liên kết, nó phải hợp lệ. –

+0

Không, đặt _definition-declaration_ trong 'main' [gây ra lỗi _compiler_] (http://codepad.org/KrhX3kHL). [Những gì Charles gợi ý là chính xác] (http://codepad.org/ERZrmowu). Và lần sau, vui lòng cung cấp một testcase mỗi khi bạn quay trở lại với một báo cáo lỗi như vậy. Xem cuốn sách C++ của bạn để biết sự khác biệt giữa khai báo và định nghĩa. –