2016-10-10 13 views
20

Mã này là từ "C++ ngôn ngữ lập trình" của Bjarne Stroustrup (C.13.8.3 Điểm õ Binding)Tại thời điểm nào xảy ra liên kết Khiếu nại mẫu?

template <class T> 
void f(T value) 
{ 
    g(value); 
} 

void g(int v); 

void h() 
{ 
    extern g(double); 
    f(2); 
} 

Và ông đề cập đến:

Here, the point of instantiation for f() is just before h(), so the g() called in f() is the global g(int) rather than the local g(double). The definition of ‘‘instantiation point’’ implies that a template parameter can never be bound to a local name or a class member.

void h() 
{ 
    struct X {}; // local structure 
    std::vector<X> v; // error: can't use local structure as template parameter 
} 

Câu hỏi của tôi là:

  1. Tại sao mã đầu tiên hoạt động? g() được khai báo sau, và tôi thực sự gặp lỗi với G ++ 4.9.2 rằng g không được khai báo tại thời điểm đó.

  2. extern g (double) - cách thức hoạt động? vì giá trị trả về không quan trọng trong trường hợp quá tải hàm, sau đó chúng ta có thể bỏ qua nó trong các khai báo chuyển tiếp?

  3. điểm khởi tạo cho f() ngay trước h() - tại sao? không phải là nó hợp lý mà nó sẽ nhận được instantiated khi f(2) đang được gọi là? Ngay nơi chúng tôi gọi nó, whence g(double) sẽ nằm trong phạm vi đã có.

  4. Định nghĩa về ‘điểm instantiation’ ngụ ý rằng thông số mẫu không bao giờ có thể bị ràng buộc với tên địa phương hoặc thành viên lớp - Điều này có thay đổi trong C++ 14 không? Tôi gặp lỗi với C++ (G ++ 4.9.2), nhưng không gặp lỗi với C++ 14 (G ++ 4.9.2).

+2

"Năm 1985, phiên bản đầu tiên của The C++ Programming Language đã được phát hành, mà đã trở thành tài liệu tham khảo dứt khoát cho ngôn ngữ, như ** đã có không một tiêu chuẩn chính thức **. " [wiki] (https://en.wikipedia.org/wiki/C%2B%2B#History) Vì vậy, nó không thay đổi giữa 'C++ 11' và' C++ 14'. Nó thay đổi giữa "chuẩn hóa trước" và tiêu chuẩn hóa. – bolov

+0

Kiểm tra 14.6.4.1 [temp.point] cho các quy tắc – AndyG

+0

cũng tìm kiếm tra cứu tên hai giai đoạn – bolov

Trả lời

13

"Năm 1985, phiên bản đầu tiên của The C++ Programming Language đã được phát hành, mà đã trở thành tài liệu tham khảo dứt khoát cho ngôn ngữ, như đã có chưa một tiêu chuẩn chính thức." wiki C++ History Vì vậy, nó không thay đổi giữa C++ 11 và C++ 14. Tôi có thể giả định (và hãy lấy điều này với một hạt muối) nó thay đổi giữa "chuẩn hóa trước" và tiêu chuẩn hóa. Có lẽ ai đó biết rõ hơn về lịch sử của C++ có thể làm sáng tỏ thêm ở đây.

Đối với những gì thực sự xảy ra:


Đầu tiên chúng ta hãy ra khỏi đường đi đơn giản:

extern g(double); 

này không hợp lệ C++. Trong lịch sử, tiếc là C cho phép bỏ sót loại. Trong C++ bạn phải viết extern void g(double).


Tiếp theo, hãy bỏ qua những quá tải g(double) để trả lời câu hỏi đầu tiên của bạn:

template <class T> 
void f(T value) 
{ 
    g(value); 
} 

void g(int v); 

int main() 
{ 
    f(2); 
} 

Trong C++ có khét tiếng hai giai đoạn tra cứu tên:

  • Trong giai đoạn đầu, tại định nghĩa mẫu, tất cả non-dependent names được giải quyết. Không làm như vậy là một lỗi nghiêm trọng;
  • Tên phụ thuộc được giải quyết trong giai đoạn hai, tại bản mẫu.

Quy tắc phức tạp hơn một chút, nhưng đó chính là ý chính của nó.

g phụ thuộc vào thông số mẫu T để nó chuyển giai đoạn đầu tiên. Điều đó có nghĩa rằng nếu bạn không bao giờ khởi tạo f, thì mã sẽ biên dịch tốt. Ở giai đoạn thứ hai, f được khởi tạo với T = int. g(int) hiện đang tìm kiếm, nhưng không tìm thấy:

17 : error: call to function 'g' that is neither visible in the template definition nor found by argument-dependent lookup 
g(value); 
^ 
24 : note: in instantiation of function template specialization 'f<int>' requested here 
f(2); 
^ 
20 : note: 'g' should be declared prior to the call site 
void g(int v); 

Để cho một cái tên tùy ý g để vượt qua với màu sắc bay chúng tôi có một vài lựa chọn:

  1. Khai g trước:
void g(int); 

template <class T> 
void f(T value) 
{ 
    g(value); 
} 
  1. mang g với T:
template <class T> 
void f(T) 
{ 
    T::g(); 
} 

struct X { 
    static void g(); 
}; 

int main() 
{ 
    X x; 
    f(x); 
} 
  1. Mang g với T qua ADL:
template <class T> 
void f(T value) 
{ 
    g(value); 
} 

struct X {}; 

void g(X); 

int main() 
{ 
    X x; 
    f(x); 
} 

Những dĩ nhiên thay đổi ngữ nghĩa của chương trình. Họ có nghĩa là để minh họa những gì bạn có thể và không thể có trong một mẫu.


Đối với lý do tại sao không ADL tìm g(int), nhưng thấy g(X):

§ 3.4.2 Argument-dependent name lookup [basic.lookup.argdep]

  1. For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered [...]:

    • If T is a fundamental type, its associated sets of namespaces and classes are both empty.

    • If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces of which its associated classes are members. [...]


Và cuối cùng chúng tôi nhận được lý do tại sao extern void g(double); bên trong chính là không tìm thấy: trước hết chúng tôi cho thấy rằng g(fundamental_type) được tìm thấy iff nó được khai báo trước định nghĩa f. Vì vậy, hãy làm cho nó void g(X) bên trong main. ADL có tìm thấy nó không?

template <class T> 
void f(T value) 
{ 
    g(value); 
} 

struct X{}; 


int main() 
{ 
    X x; 
    void g(X); 

    f(x); 
} 

số Bởi vì nó không nằm trong không gian tên giống như X (ví dụ: namespace toàn cục) ADL không thể tìm thấy nó.

Proof rằng g không có trong thế giới

int main() 
{ 
    void g(X); 

    X x; 
    g(x); // OK 
    ::g(x); // ERROR 
} 

34 : error: no member named 'g' in the global namespace; did you mean simply 'g'?

+0

'h' sẽ bị vô hiệu, tôi đã sửa nó. – user1289

+0

Cảm ơn bạn đã trả lời. Tôi có hai câu hỏi. 1. Tại sao trong ví dụ đầu tiên 'g' là một người không phụ thuộc? Nó phụ thuộc từ T (trong trường hợp này int), với ADL nó có thể được tìm thấy trong không gian tên T được định nghĩa (cho không gian tên được xây dựng trong kiểu được cho là không gian tên chung, theo như tôi biết). 2. Với logic của bạn, tại sao 'f (x)' hoạt động, nhưng 'f (2)' không hoạt động với ADL? – user1289

+1

@ user1289: 'f (2)' không hoạt động với ADL vì '2' là' int', là kiểu cơ bản và "1) Đối với các đối số kiểu cơ bản, tập hợp các không gian tên và lớp được liên kết trống "(Xem 3.9.1 Các loại cơ bản [basic.fundamental]) – AndyG

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