2009-10-21 34 views
79

Thỉnh thoảng tôi đã nhìn thấy một số thông báo lỗi thực sự không thể giải mã được thoát ra bởi gcc khi sử dụng mẫu ... Cụ thể, tôi đã gặp sự cố khi khai báo có vẻ chính xác gây ra lỗi biên dịch kỳ lạ đã biến mất bởi tiền tố "typename" từ khóa đến đầu khai báo ... (Ví dụ, chỉ tuần trước, tôi đã tuyên bố hai vòng lặp là thành viên của một lớp templated khác và tôi phải làm điều này) ...Chính thức, tên tệp là gì?

Câu chuyện về tên tệp là gì?

+3

http://stackoverflow.com/questions/1600464/ – sbi

Trả lời

124

Sau đây là trích dẫn từ Josuttis cuốn sách :

Tên tệp từ khóa được giới thiệu đến chỉ định rằng mã nhận dạng là một loại. Hãy xem xét các ví dụ sau:

template <class T> 
Class MyClass 
{ 
    typename T::SubType * ptr; 
    ... 
}; 

Ở đây, typename được sử dụng để làm rõ rằng Loại con: là một loại lớp T. Như vậy, ptr là con trỏ đến kiểu T :: subtype. Nếu không có tên tệp, SubType sẽ được coi là thành viên tĩnh. Như vậy

T::SubType * ptr 

sẽ là một phép nhân có giá trị SubType của loại T với ptr.

+1

Sách hay. Đọc nó thông qua một lần sau đó giữ nó như là một tài liệu tham khảo nếu bạn thích. –

+0

câu trả lời hay !!! – Gob00st

20

Stan Lippman's BLog post gợi ý: -

Stroustrup tái sử dụng hiện tại lớp từ khóa để xác định một tham số kiểu chứ không phải giới thiệu một từ khóa mới mà có thể phá vỡ tất nhiên chương trình hiện có. Nó không phải là một từ khóa mới không được xem xét - chỉ là nó không được coi là cần thiết cho sự gián đoạn tiềm năng của nó. Và cho đến khi đạt chuẩn ISO-C++, đây là cách duy nhất tuyên bố thông số loại.

Vì vậy, về cơ bản Stroustrup tái sử dụng từ khoá class mà không giới thiệu một từ khóa mới được thay đổi sau đó trong tiêu chuẩn vì những lý do sau đây

Như ví dụ đưa ra

template <class T> 
class Demonstration { 
public: 
void method() { 
    T::A *aObj; // oops … 
    // … 
}; 

ngôn ngữ ngữ pháp hiểu sai T::A *aObj; như một biểu thức số học để từ khóa mới được giới thiệu được gọi là typename

typename T::A* a6; 

nó chỉ thị trình biên dịch xử lý câu lệnh tiếp theo dưới dạng khai báo.

Do từ khóa là trong biên chế, quái gì, tại sao không giải quyết được những rắc rối gây ra bởi quyết định ban đầu để tái sử dụng các từ khóa lớp.

Thats lý do tại sao chúng tôi có cả

Bạn có thể có một cái nhìn tại this post, nó chắc chắn sẽ giúp bạn, tôi chỉ chiết xuất từ ​​nó càng nhiều càng tốt mà tôi có thể

+0

Có, nhưng sau đó tại sao là một từ khóa mới 'typename' cần thiết, nếu bạn có thể sử dụng từ khóa hiện 'class' với mục đích giống nhau không? – Jesper

+4

@ Jesper: Tôi nghĩ câu trả lời của Xenus là khó hiểu ở đây. 'typename' trở nên cần thiết để khắc phục vấn đề phân tích cú pháp như được mô tả trong câu trả lời của Naveen bằng cách trích dẫn Josuttis. (Tôi không nghĩ rằng việc chèn một 'lớp' vào nơi này sẽ có tác dụng.) Chỉ sau khi từ khóa mới được chấp nhận cho trường hợp này, nó cũng được cho phép trong khai báo đối số khuôn mẫu (_or là định nghĩa? _), Bởi vì đó' class' luôn có phần gây hiểu lầm. – sbi

11

xem xét mã

template<class T> somefunction(T * arg) 
{ 
    T::sometype x; // broken 
    . 
    . 

Thật không may, trình biên dịch là không bắt buộc phải tâm linh, và không biết liệu T :: sometype sẽ kết thúc đề cập đến một loại tên hoặc một thành viên tĩnh của T. Vì vậy, người ta sử dụng typename nói với nó:

template<class T> somefunction(T * arg) 
{ 
    typename T::sometype x; // works! 
    . 
    . 
4

Hai công dụng:

  1. Là một từ khóa mẫu tranh luận (thay vì 'lớp')
  2. Một từ khóa typename nói với trình biên dịch rằng một định danh là một loại (chứ không phải là một biến thành viên tĩnh)
template <typename T> class X // [1] 
{ 
    typename T::Y _member; // [2] 
} 
3

Các bí mật nằm trong thực tế là một mẫu có thể được chuyên biệt cho một số loại. Điều này có nghĩa là nó cũng có thể xác định giao diện hoàn toàn khác nhau đối với một số loại. Ví dụ: bạn có thể viết:

template<typename T> 
struct test { 
    typedef T* ptr; 
}; 

template<>   // complete specialization 
struct test<int> { // for the case T is int 
    T* ptr; 
}; 

Người ta có thể hỏi tại sao điều này hữu ích và thực sự: Điều đó thực sự trông vô dụng. Nhưng hãy nhớ rằng ví dụ: std::vector<bool> loại reference trông hoàn toàn khác so với các loại khác T s. Phải thừa nhận rằng nó không thay đổi loại reference từ một loại thành một cái gì đó khác nhau nhưng tuy nhiên nó có thể xảy ra.

Bây giờ điều gì xảy ra nếu bạn viết mẫu của riêng mình bằng mẫu test này. Một cái gì đó như thế này

template<typename T> 
void print(T& x) { 
    test<T>::ptr p = &x; 
    std::cout << *p << std::endl; 
} 

nó có vẻ là ok cho em bởi vì em mong đợi rằng test<T>::ptr là một loại. Nhưng trình biên dịch không biết và trong hành động, anh ta thậm chí còn được khuyên theo tiêu chuẩn để mong đợi điều ngược lại, test<T>::ptr không phải là một loại. Để nói với trình biên dịch những gì bạn mong đợi bạn phải thêm một typename trước đây. Mẫu chính xác trông giống như thế này

template<typename T> 
void print(T& x) { 
    typename test<T>::ptr p = &x; 
    std::cout << *p << std::endl; 
} 

Dòng dưới cùng: Bạn phải thêm typename trước khi bạn sử dụng loại lồng nhau của mẫu trong mẫu của mình. (Tất nhiên chỉ khi tham số mẫu của mẫu của bạn được sử dụng cho mẫu bên trong đó.)

5

Trong một số trường hợp, bạn tham khảo một thành viên được gọi là tùy thuộc vào loại (có nghĩa là "phụ thuộc vào thông số mẫu") trình biên dịch không thể luôn luôn suy luận rõ ràng ý nghĩa ngữ nghĩa của cấu trúc kết quả, bởi vì nó không biết tên đó là gì (nghĩa là nó là tên của một kiểu, tên của một thành viên dữ liệu hay tên của cái gì khác). Trong các trường hợp như vậy bạn phải phân biệt tình hình bằng cách nói rõ ràng trình biên dịch rằng tên thuộc về một tên tệp được định nghĩa là một thành viên của kiểu phụ thuộc đó.

Ví dụ

template <class T> struct S { 
    typename T::type i; 
}; 

Trong ví dụ này các từ khóa typename trong cần thiết cho việc mã để biên dịch.

Điều tương tự cũng xảy ra khi bạn muốn tham chiếu đến thành viên mẫu thuộc loại phụ thuộc, tức là tên chỉ định mẫu. Bạn cũng phải giúp trình biên dịch bằng cách sử dụng các từ khóa template, mặc dù nó được đặt khác nhau

template <class T> struct S { 
    T::template ptr<int> p; 
}; 

Trong một số trường hợp nó có thể là cần thiết để sử dụng cả hai

template <class T> struct S { 
    typename T::template ptr<int>::type i; 
}; 

(nếu tôi nhận được cú pháp chính xác) .

Tất nhiên, một vai trò khác của từ khóa typename sẽ được sử dụng trong khai báo tham số mẫu.

+0

Xem thêm [Mô tả về từ khóa tên tập tin C++] (http://pages.cs.wisc.edu/~driscoll/typename.html) để biết thêm thông tin (nền). – Atafar

1
#include <iostream> 

class A { 
public: 
    typedef int my_t; 
}; 

template <class T> 
class B { 
public: 
    // T::my_t *ptr; // It will produce compilation error 
    typename T::my_t *ptr; // It will output 5 
}; 

int main() { 
    B<A> b; 
    int my_int = 5; 
    b.ptr = &my_int; 
    std::cout << *b.ptr; 
    std::cin.ignore(); 
    return 0; 
} 
Các vấn đề liên quan