2012-04-20 14 views
16
#include <iostream> 

void foo() 
{ 
    std::cout << "global foo()" << std::endl; 
} 

struct A { 
    void foo() 
    { 
     std::cout << "A::foo()" << std::endl; 
    } 
}; 

struct B : public A { 
    void call() 
    { 
     foo(); 
    } 
}; 

int main(int argc, char **argv) 
{ 
    B b; 
    b.call(); 
    return 0; 
} 

Điều này cho phép expected result:Thứ tự tìm kiếm phạm vi biểu tượng C++ khác nhau đối với mẫu và lớp không phải mẫu?

A::foo() 

Tuy nhiên sau khi thay đổi hai dòng (lớp B để mẫu):

#include <iostream> 

void foo() 
{ 
    std::cout << "global foo()" << std::endl; 
} 

struct A { 
    void foo() 
    { 
     std::cout << "A::foo()" << std::endl; 
    } 
}; 

template <typename T> // change here 
struct B : public T { 
    void call() 
    { 
     foo(); 
    } 
}; 

int main(int argc, char **argv) 
{ 
    B<A> b; // and here 
    b.call(); 
    return 0; 
} 

tôi nhận được unexpected result:

global foo() 

Và sử dụng this-> là không phải là một lựa chọn như tôi đang cố gắng để cre ăn một cơ chế "dự phòng".

+0

Thật thú vị. –

+0

Sẽ 'gọi() {T :: foo(); } 'có được sử dụng không? Nó hoạt động. – chris

+0

Cách gọi 'T :: foo();' từ 'B :: call'? – mfontanini

Trả lời

16

Những gì bạn nhận được là kết quả mong đợi. Điều này được gọi là "tra cứu tên hai pha" trong tiêu chuẩn C++.

Tên bên trong các mẫu được chia thành hai loại:

phụ thuộc - tên mà phụ thuộc vào các thông số mẫu nhưng không được khai báo bên trong mẫu.

Không phụ thuộc - tên không phụ thuộc vào thông số mẫu, cộng với tên của bản thân mẫu và tên được khai báo trong đó.

Khi trình biên dịch cố gắng giải quyết một số tên trong mã, trước tiên nó quyết định xem tên có phụ thuộc hay không và quá trình giải quyết bắt nguồn từ sự khác biệt này. Trong khi các tên không phụ thuộc được giải quyết "bình thường" - khi mẫu được xác định, độ phân giải cho các tên phụ thuộc sẽ xảy ra tại thời điểm tạo bản mẫu.

foo(); trong B::call trong ví dụ của bạn là tên không phụ thuộc, do đó, nó được giải quyết toàn cầu foo() tại điểm định nghĩa mẫu.

+0

Làm thế nào để làm việc với bình luận của Jagannath "Nó hoạt động tốt VS 11 Beta. Các cuộc gọi A :: foo()."? Tôi biết MS không phải là điều tốt nhất để xác nhận bất kỳ tiêu chuẩn nào chống lại. Ngoài ra bất kỳ tài liệu tham khảo để C + + (03) tiêu chuẩn sẽ được tốt đẹp. Và bạn có biết cách làm cho nó hoạt động như tôi muốn không? – elmo

+3

@elmo: Microsoft C++ không thực hiện tra cứu tên hai giai đoạn một cách chính xác. Xem [câu hỏi này] (http://stackoverflow.com/questions/6273176). –

+0

@elmo: Để làm cho nó làm việc theo cách của bạn: mẫu struct B: T công cộng { khoảng trống gọi() { T :: foo(); // Thêm T :: vào mã } }; – CppLearner

0

Bạn cần phải thông báo cụ thể để sử dụng phương pháp lớp T.

template <typename T> 
struct B : public T { 
    void call() 
    { 
     T::foo(); 
    } 
}; 


Nhưng như đối với một cơ chế dự phòng, bạn có thể kiểm tra câu hỏi này: Is it possible to write a template to check for a function's existence?

Sử dụng một thất bại Thay không phải là một lỗi (SFINAE), bạn có thể kiểm tra cho phương thức foo trong T, rồi chạy phương thức thích hợp.

+0

Vui lòng xem nhận xét của anh ấy và dòng cuối cùng của câu hỏi của anh ấy. – Jagannath

+0

Một lần nữa: dòng cuối cùng trong câu hỏi của tôi nói rõ ràng nó sẽ không hoạt động: http://ideone.com/YuoIH – elmo

+0

@elmo: tại sao bạn ngoại trừ nó hoạt động khi '' C'' cấu trúc không có '' foo'' phương pháp? http://ideone.com/WcLQE – fogbit

3

Câu trả lời được chấp nhận giải thích lý do bạn thấy hành vi đó, nhưng không phải là cách đạt được hành vi "dự phòng" mà bạn muốn. Điều đó có thể được thực hiện bằng cách sử dụng SFINAE, bằng cách giới thiệu một cặp quá tải mẫu thành viên, một trong số đó sẽ chỉ tồn tại nếu lớp cơ sở có hàm thành viên được gọi là foo.

template <typename T> 
struct B : T { 
    template <void (T::*)()> struct has_mem_fn {}; 

    template <typename U> void call(has_mem_fn<&U::foo>*) {this->foo();} 
    template <typename U> void call(...) {foo();} 

    void call() {call<T>(0);} 
}; 

struct X {}; 

int main() 
{ 
    B<A> ba; 
    ba.call(); // A::foo() 

    B<X> bx; 
    bx.call(); // global foo() 
} 

UPDATE: nơi bạn nói bạn biết phương pháp này, nhưng không thể sử dụng nó do phải hỗ trợ trình biên dịch rối loạn chức năng. Trong trường hợp đó, tôi sợ rằng những gì bạn muốn có lẽ là không thể.

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