2011-10-27 39 views
43

thể trùng lặp:
Officially, what is typename for?
Where and why do I have to put the template and typename keywords?Khi nào là từ khóa "tên tệp" cần thiết?

xem xét các mã bên dưới:

template<class K> 
class C { 
    struct P {}; 
    vector<P> vec; 
    void f(); 
}; 

template<class K> void C<K>::f() { 
    typename vector<P>::iterator p = vec.begin(); 
} 

Tại sao là "typename" từ khóa cần thiết trong ví dụ này? Có trường hợp nào khác phải xác định "tên tệp" không?

+0

http://stackoverflow.com/questions/610245/where-and-why-do- i-have-to-put-template-and-typename-on-dependent-name –

Trả lời

58

Câu trả lời ngắn: Bất cứ khi nào đề cập đến tên lồng nhau là tên phụ thuộc, tức là lồng trong mẫu bản mẫu có tham số không xác định.

Câu trả lời dài: Có ba cấp thực thể trong C++: giá trị, loại và mẫu. Tất cả những cái đó có thể có tên, và cái tên đó không nói cho bạn biết tầng thực thể nào của nó. Thay vào đó, thông tin về bản chất của thực thể tên phải được suy ra từ ngữ cảnh.

Bất cứ khi nào suy luận này là không thể, bạn phải xác định nó:

template <typename> struct Magic; // defined somewhere else 

template <typename T> struct A 
{ 
    static const int value = Magic<T>::gnarl; // assumed "value" 

    typedef typename Magic<T>::brugh my_type; // decreed "type" 
    //  ^^^^^^^^ 

    void foo() { 
    Magic<T>::template kwpq<T>(1, 'a', .5); // decreed "template" 
    //  ^^^^^^^^ 
    } 
}; 

đây tên Magic<T>::gnarl, Magic<T>::brughMagic<T>::kwpq đã được expliciated, bởi vì nó không thể nói: Kể từ Magic là một mẫu, các rất thiên nhiên thuộc loại Magic<T> tùy thuộc vào T - có thể có các chuyên ngành hoàn toàn khác với mẫu chính, chẳng hạn.

Điều gì làm cho Magic<T>::gnarl tên phụ thuộc là thực tế rằng chúng tôi đang ở trong định nghĩa mẫu, trong đó T không xác định. Nếu chúng ta sử dụng Magic<int>, điều này sẽ khác, vì trình biên dịch biết (bạn hứa!) định nghĩa đầy đủ của Magic<int>.

(Nếu bạn muốn tự mình kiểm tra, đây là định nghĩa mẫu của Magic mà bạn có thể sử dụng. Hãy tha thứ sử dụng constexpr trong đặc biệt cho ngắn gọn; nếu bạn có trình biên dịch cũ, hãy thay đổi hằng số thành viên tĩnh khai báo với pre C-++ hình thức kiểu cũ 11)

template <typename T> struct Magic 
{ 
    static const T     gnarl; 
    typedef T &      brugh; 
    template <typename S> static void kwpq(int, char, double) { T x; } 
}; 
template <> struct Magic<signed char> 
{ 
    // note that `gnarl` is absent 
    static constexpr long double brugh = 0.25; // `brugh` is now a value 
    template <typename S> static int kwpq(int a, int b) { return a + b; } 
}; 

cách sử dụng:.

int main() 
{ 
    A<int> a; 
    a.foo(); 

    return Magic<signed char>::kwpq<float>(2, 3); // no disambiguation here! 
} 
+0

@Nils: 'const' sẽ không hợp lệ C++. –

+0

Cảm ơn bạn đã giải thích. Nhưng tôi không thể hiểu một điều cuối cùng: như tên 'Magic :: kwpq' phụ thuộc vào tham số mẫu' T', điều này sẽ được giải quyết trong giai đoạn thứ hai của * tra cứu hai pha *, phải không? Vậy tại sao trình biên dịch không thể kiểm tra tại thời điểm đó (khi anh ta có thể xác định liệu 'kwpq' là một mẫu hay một kiểu hay bất kỳ thứ gì) nếu tên được sử dụng đúng theo cấp * của thực thể *? –

+0

@PaoloM: Định nghĩa hàm thành viên của mẫu lớp 'A' phải phân tích cú pháp đã có trong giai đoạn đầu tiên, vì vậy bạn cần định hướng cho cú pháp có ý nghĩa. Trong giai đoạn thứ hai, chúng ta kiểm tra sự khởi tạo 'Magic ' đối với loại bê tông 'T' thực sự có một thành viên' kwpq' là một hàm có chữ ký thích hợp. (Và cảm ơn cho chỉnh sửa!) –

7

Các từ khóa typename là cần thiết bất cứ khi nào một tên loại phụ thuộc vào tham số mẫu (do đó trình biên dịch có thể 'biết' ngữ nghĩa của một định danh (loại hoặc giá trị) mà không có một bảng biểu tượng đầy đủ tại là người đầu tiên vượt qua).


Không có trong ý nghĩa tương tự, và một chút ít phổ biến hơn, các duy typename từ khóa cũng có thể hữu ích khi sử dụng chung mẫu số: http://ideone.com/amImX

#include <string> 
#include <list> 
#include <vector> 

template <template <typename, typename> class Container, 
      template <typename> class Alloc = std::allocator> 
struct ContainerTests 
{ 
    typedef Container<int, Alloc<int> > IntContainer; 
    typedef Container<std::string, Alloc<int> > StringContainer; 
    // 
    void DoTests() 
    { 
     IntContainer ints; 
     StringContainer strings; 
     // ... etc 
    } 
}; 

int main() 
{ 
    ContainerTests<std::vector> t1; 
    ContainerTests<std::list> t2; 

    t1.DoTests(); 
    t2.DoTests(); 
} 
+0

Tôi không bao giờ biết ab ra "tên tập tin đơn độc" trước đây, điều đó thật tuyệt! –

10

Từ khóa typename, là cần thiết vì iterator là loại phụ thuộc trên P. Trình biên dịch không thể đoán nếu iterator đề cập đến một giá trị hoặc một loại, do đó, nó giả định giá trị của nó trừ khi bạn la hét typename. Nó là cần thiết bất cứ khi nào có một loại phụ thuộc vào một đối số mẫu, trong một bối cảnh mà một trong hai loại hoặc giá trị sẽ là hợp lệ. Ví dụ, vì các lớp cơ sở typename là không cần thiết vì một lớp cơ sở phải là một kiểu.

Trên cùng một chủ đề, có một từ khóa template được sử dụng để cho trình biên dịch biết rằng một số tên phụ thuộc là một hàm mẫu thay vì một giá trị.

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