2010-09-24 38 views
68

Tôi đã chơi với tiếng kêu một lúc và tôi tình cờ gặp "test/SemaTemplate/dependent-template-recovery.cpp" (trong bản phân phối tiếng kêu) được cho là cung cấp gợi ý để khôi phục từ mẫu lỗi.Lỗi mẫu khó hiểu

Toàn bộ điều có thể dễ dàng lột xuống một ví dụ nhỏ:

template<typename T, typename U, int N> struct X { 
    void f(T* t) 
    { 
     // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}} 
     t->f0<U>(); 
    } 
}; 

Thông báo lỗi mang lại bởi kêu vang:

tpl.cpp:6:13: error: use 'template' keyword to treat 'f0' as a dependent template name 
     t->f0<U>(); 
      ^
      template 
1 error generated. 

... Nhưng tôi có một thời gian hiểu biết khó mà chính xác một trong những nghĩa vụ phải chèn các từ khóa template để có mã được cú pháp chính xác?

+9

Bạn đã thử chèn nó vào nơi mũi tên chỉ? –

+3

Tương tự như [this] (http://stackoverflow.com/questions/3691420/compiler-error-when-using-integer-as-template-parameter/) và [this] (http://stackoverflow.com/questions/3621719/c-mẫu-cú pháp) –

Trả lời

65

ISO C++ 03 14,2/4:

Khi tên của một thành viên mẫu chuyên môn xuất hiện sau. hoặc -> trong biểu thức postfix, hoặc sau khi nested-name-specifier trong một id đủ điều kiện và biểu thức postfix hoặc id đủ điều kiện rõ ràng phụ thuộc vào tham số mẫu (14.6.2), tên mẫu thành viên phải được bắt đầu bằng mẫu từ khóa. Nếu không, tên được giả định là đặt tên cho một mẫu không phải là mẫu.

Trong t->f0<U>();f0<U> là thành viên mẫu chuyên môn mà xuất hiện sau khi -> và đó rõ ràng phụ thuộc vào tham số mẫu U, vì vậy các thành viên mẫu chuyên môn phải có tiền tố template từ khóa.

Vì vậy, hãy thay đổi t->f0<U>() thành t->template f0<U>().

+0

Thật thú vị, tôi nghĩ đặt biểu thức trong dấu ngoặc đơn: 't -> (f0 ())' sẽ cố định điều đó, như tôi nghĩ rằng sẽ đặt 'f0 ()' vào độc lập biểu hiện ... tốt, tôi nghĩ sai, có vẻ như ... –

+5

Bạn có thể nhận xét về lý do tại sao đây là trường hợp? Tại sao C++ yêu cầu loại cú pháp này? – Curious

7

Chèn ngay trước điểm mà caret là:

template<typename T, typename U, int N> struct X { 
    void f(T* t) 
    { 
     t->template f0<U>(); 
    } 
}; 

Chỉnh sửa: lý do cho quy tắc này trở nên rõ ràng hơn nếu bạn suy nghĩ như một trình biên dịch. Trình biên dịch thường chỉ nhìn về phía trước một hoặc hai mã thông báo cùng một lúc và không thường "nhìn về phía trước" với phần còn lại của biểu thức. [Chỉnh sửa: xem bình luận] Lý do cho từ khóa giống như lý do tại sao bạn cần từ khóa typename để chỉ ra tên kiểu phụ thuộc: nó nói với trình biên dịch "này, số nhận dạng bạn sắp xem là tên của mẫu, thay vì tên của một thành viên dữ liệu tĩnh theo sau là một dấu nhỏ hơn ".

+0

Tôi sẽ có ** không bao giờ ** đã có thể đoán rằng ... nhưng cảm ơn bạn ;-). luôn có điều gì đó để tìm hiểu về C++! –

+3

Ngay cả với giao diện vô hạn, bạn vẫn cần 'mẫu'. Có những trường hợp cả khi có và không có 'template' sẽ mang lại các chương trình hợp lệ với hành vi khác nhau. Vì vậy, đây không chỉ là một vấn đề cú pháp ('t-> f0 (0)' là cú pháp hợp lệ cho cả phiên bản danh sách đối số nhỏ hơn và mẫu). –

+0

@Johannes Schaub - litb: Phải, do đó, nó là một vấn đề của việc gán ý nghĩa ngữ nghĩa nhất quán cho biểu thức, hơn là nhìn về phía trước. – Doug

6

Trích từ C++ Templates

Các .template Xây dựng Một vấn đề rất tương tự cũng được phát hiện sau sự ra đời của typename. Hãy xem ví dụ sau bằng cách sử dụng loại bitet tiêu chuẩn:

template<int N> 
void printBitset (std::bitset<N> const& bs) 
{ 
    std::cout << bs.template to_string<char,char_traits<char>, 
             allocator<char> >(); 
} 

Cấu trúc lạ trong ví dụ này là .template. Nếu không có sử dụng thêm mẫu, trình biên dịch không biết rằng mã thông báo nhỏ hơn (<) mà sau đây không thực sự là "ít hơn" nhưng bắt đầu của một danh sách đối số mẫu. Lưu ý rằng đây chỉ là vấn đề nếu cấu trúc trước thời hạn phụ thuộc vào tham số mẫu. Trong ví dụ của chúng tôi, tham số bs phụ thuộc vào thông số mẫu N.

Kết luận. trên thông số mẫu.

+0

thực sự, 1 cho ví dụ tuyệt vời –

19

Bên cạnh những điểm khác thực hiện, nhận thấy rằng đôi khi trình biên dịch không thể tạo nên tâm trí của mình và cả những giải thích có thể mang lại các chương trình hợp lệ thay thế khi instantiating

#include <iostream> 

template<typename T> 
struct A { 
    typedef int R(); 

    template<typename U> 
    static U *f(int) { 
    return 0; 
    } 

    static int f() { 
    return 0; 
    } 
}; 

template<typename T> 
bool g() { 
    A<T> a; 
    return !(typename A<T>::R*)a.f<int()>(0); 
} 


int main() { 
    std::cout << g<void>() << std::endl; 
} 

này in 0 khi bỏ qua template trước f<int()> nhưng 1 khi lắp. Tôi để nó như là một bài tập để tìm hiểu xem mã đó làm gì.

+1

Bây giờ đó là một ví dụ đáng sợ! –

+1

Tôi không thể sao chép hành vi bạn mô tả trong Visual Studio 2013. Nó luôn gọi 'f ' và luôn in '1', điều này hoàn toàn hợp lý với tôi. Tôi vẫn không hiểu lý do tại sao từ khóa 'template' là bắt buộc và sự khác biệt của nó. –

+0

@Violet trình biên dịch VSC++ không phải là trình biên dịch C++ phù hợp. Một câu hỏi mới là cần thiết nếu bạn muốn biết tại sao VSC++ luôn in 1. –

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