2010-06-01 5 views
70

Phần nào của đặc tả C++ hạn chế tra cứu phụ thuộc đối số từ việc tìm kiếm mẫu hàm trong tập hợp các không gian tên được liên kết? Nói cách khác, tại sao cuộc gọi cuối cùng ở số main bên dưới không thể biên dịch được?Tại sao ADL không tìm thấy mẫu chức năng?

namespace ns { 
    struct foo {}; 
    template<int i> void frob(foo const&) {} 
    void non_template(foo const&) {} 
} 

int main() { 
    ns::foo f; 
    non_template(f); // This is fine. 
    frob<0>(f); // This is not. 
} 
+0

Liệu nó có nghĩa, mà bạn mong đợi để làm việc frob() mà không cần viết ns :: frob()? – Simon

+0

Có, theo cách thức của một chức năng không phải mẫu. – Hugh

+0

FYI mã trên không thành công trong Comeau cũng như: http://www.comeaucomputing.com/tryitout/ - thêm 'sử dụng không gian tên ns;' hoặc 'ns ::' trình độ vượt qua biên dịch. Đây là một câu hỏi hay. – fbrereto

Trả lời

74

phần này sẽ giải thích nó:

C++ chuẩn 03 14.8.1.6:

[Lưu ý: Đối với tên hàm đơn giản, lập luận tra cứu phụ thuộc (3.4.2) được áp dụng ngay cả khi tên chức năng không hiển thị trong phạm vi cuộc gọi. Điều này là do cuộc gọi vẫn có dạng cú pháp của một cuộc gọi hàm (3.4.1). Nhưng khi một mẫu chức năng với các đối số mẫu rõ ràng được sử dụng, cuộc gọi không có dạng cú pháp chính xác trừ khi có một mẫu chức năng với tên đó hiển thị tại điểm của cuộc gọi. Nếu không có tên như vậy có thể nhìn thấy, cuộc gọi không phải là cú pháp được hình thành tốt và tra cứu phụ thuộc vào đối số không áp dụng. Nếu một số tên như vậy được hiển thị, tra cứu phụ thuộc đối số áp dụng và các mẫu chức năng bổ sung có thể được tìm thấy trong các không gian tên khác.

namespace A { 
    struct B { }; 
    template<int X> void f(B); 
} 
namespace C { 
    template<class T> void f(T t); 
} 
void g(A::B b) { 
    f<3>(b); //ill-formed: not a function call 
    A::f<3>(b); //well-formed 
    C::f<3>(b); //ill-formed; argument dependent lookup 
       // applies only to unqualified names 
    using C::f; 
    f<3>(b); //well-formed because C::f is visible; then 
       // A::f is found by argument dependent lookup 
} 
+4

+1 Câu trả lời hay. – fbrereto

+0

Tuyệt vời - cảm ơn bạn. – Hugh

+0

Ah, tôi không thể tìm thấy tài liệu tham khảo đó :) +1 –

0

Chỉnh sửa: Không, điều này không đúng. Xem @Kornel's answer.


Tôi không hoàn toàn chắc chắn nhưng sau khi tham khảo ý kiến ​​Stroustrup của "Ngôn ngữ lập trình C++" Tôi nghĩ rằng phần Phụ lục C 13.8.4 thể thể là nguyên nhân.

frob là mẫu, người ta có thể tưởng tượng nó chuyên cho i=0 tại một thời điểm sau khi bạn gọi. Điều này có nghĩa là việc thực hiện sẽ được để lại bằng hai cách có thể chọn số frob để gọi vì nó xuất hiện nó có thể chọn nó tại điểm instantiation hoặc vào cuối xử lý đơn vị dịch.

Vì vậy, tôi nghĩ rằng vấn đề là bạn có thể làm

namespace ns { 
    struct foo {}; 
    template<int i> void frob(foo const&) {} 
} 

int main() { 
    ns::foo f; 
    frob<0>(f); 
    return 0; 
} 

namespace ns { 
    template<> void frob<0>(foo const&) { /* Do something different*/ } 
} 
+0

Không, không có không gian tên và bạn vẫn có vấn đề của bạn, phải không? Chuyên môn hóa sau khi sử dụng là một vấn đề bình thường trong C++, biểu mẫu chuyên biệt không được sử dụng nếu được khai báo sau. –

+0

@ Kornel: À vâng, điều đó đưa ra một lỗi khác, một lỗi khác phù hợp với những gì tôi mô tả. Đủ công bằng, cảm ơn vì đã chỉ ra điều đó. – Troubadour

3

Tôi muốn tinh chỉnh câu trả lời hơi chấp nhận. Nó không phải là rõ ràng trong câu hỏi OP, nhưng phần quan trọng so với tiêu chuẩn (trích dẫn bởi Kornel) là thế này (tôi nhấn mạnh):

Nhưng khi một hàm mẫu với đối số mẫu rõ ràng được sử dụng, cuộc gọi không có mẫu cú pháp chính xác

vì vậy những gì bị cấm là dựa vào ADL và sử dụng các đối số mẫu rõ ràng. Thật không may khi sử dụng các đối số mẫu không phải kiểu yêu cầu sử dụng các đối số tường minh (trừ khi chúng có các giá trị mặc định).

Dưới đây là mẫu mã cho thấy này .:

[live]

#include <string> 
#include <utility> 

namespace C { 
    struct B { }; 
    template<class T> void f(T t){} 
} 

void g(C::B b) { 
    f(b);   // OK 
    //f<C::B>(b); // ill-formed: not a function call, but only 
        // because explicit template argument were used 

    std::string s; 
    move(s);      // OK 
    //move<std::string&>(s);  // Error, again because 
           // explicit template argument were used 
    std::move<std::string&>(s); // Ok 
} 

int main() 
{ 
C::B b; 
g(b); 
}