Sự khác biệt của từ khóa 'typename' và 'class' trong các mẫu?


504

Đối với các mẫu tôi đã thấy cả hai khai báo:

template < typename T >
template < class T >

Có gì khác biệt?

Và chính xác những từ khóa đó có ý nghĩa gì trong ví dụ sau (lấy từ bài viết Wikipedia tiếng Đức về các mẫu)?

template < template < typename, typename > class Container, typename Type >
class Example
{
     Container< Type, std::allocator < Type > > baz;
};

Câu trả lời:


430

typenameclasscó thể hoán đổi cho nhau trong trường hợp cơ bản chỉ định mẫu:

template<class T>
class Foo
{
};

template<typename T>
class Foo
{
};

là tương đương.

Đã nói rằng, có những trường hợp cụ thể có sự khác biệt giữa typenameclass.

Đầu tiên là trong trường hợp của các loại phụ thuộc. typenameđược sử dụng để khai báo khi bạn đang tham chiếu một kiểu lồng nhau phụ thuộc vào một tham số mẫu khác, chẳng hạn như typedeftrong ví dụ này:

template<typename param_t>
class Foo
{
    typedef typename param_t::baz sub_t;
};

Câu hỏi thứ hai bạn thực sự thể hiện trong câu hỏi của mình, mặc dù bạn có thể không nhận ra nó:

template < template < typename, typename > class Container, typename Type >

Khi chỉ định một mẫu mẫu , classtừ khóa PHẢI được sử dụng như trên - nó không thể thay thế cho nhau typenametrong trường hợp này (lưu ý: vì C ++ 17 cả hai từ khóa đều được cho phép trong trường hợp này) .

Bạn cũng phải sử dụng classkhi khởi tạo một cách rõ ràng một mẫu:

template class Foo<int>;

Tôi chắc chắn rằng có những trường hợp khác mà tôi đã bỏ lỡ, nhưng điểm mấu chốt là: hai từ khóa này không tương đương và đây là một số trường hợp phổ biến mà bạn cần sử dụng cái này hoặc cái kia.


45
Cái cuối cùng đó là một trường hợp đặc biệt của thực tế là bạn phải sử dụng lớp hoặc struct, không phải kiểu chữ, để xác định một lớp. Rõ ràng là không thể thay thế hai bit mã đầu tiên của bạn template <typename T> typename Foo {};, bởi vì Foo <T> chắc chắn là một lớp.
Steve Jessop

2
std::vector<int>::value_typekhông phải là một loại phụ thuộc, bạn không cần typenameở đó - bạn chỉ cần nó nếu một loại phụ thuộc vào một tham số mẫu, giả sửtemplate<class T> struct C { typedef typename std::vector<T>::value_type type; };
Georg Fritzsche

2
Và một lần nữa, param_tkhông phải là một loại phụ thuộc. Các loại phụ thuộc là các tên phụ thuộc vào một tham số mẫu , ví dụ foo<param_t>::some_type, không phải chính các tham số mẫu.
Georg Fritzsche

2
Một đề xuất C ++ 1z N4051 sẽ cho phép bạn sử dụng typename, tức là template <typename> typename C.
dùng4112979

4
Kể từ đó GCC 5, G ++ hiện cho phép tên kiểu chữ trong một tham số mẫu mẫu .
Chnossos

95

Để đặt tên tham số mẫu, typenameclasslà tương đương. §14.1.2:

Không có sự khác biệt về ngữ nghĩa giữa lớp và tên kiểu chữ trong một tham số mẫu.

typenametuy nhiên có thể trong một ngữ cảnh khác khi sử dụng các mẫu - để gợi ý trình biên dịch mà bạn đang đề cập đến một loại phụ thuộc. §14.6.2:

Tên được sử dụng trong khai báo hoặc định nghĩa mẫu và phụ thuộc vào tham số mẫu được giả sử không đặt tên loại trừ khi tra cứu tên hiện hành tìm thấy tên loại hoặc tên đủ điều kiện theo tên kiểu từ khóa.

Thí dụ:

typename some_template<T>::some_type

Nếu không có typenametrình biên dịch thì không thể nói chung là bạn có tham khảo một loại hay không.


2
Tôi hiểu quy tắc, nhưng chính xác thì điều gì ngăn trình biên dịch đối xử với some_template <T> như một kiểu bên trong? Xin lỗi nếu tôi đang thiếu một cái gì đó rõ ràng.
batbrat

23

Trong khi không có sự khác biệt về kỹ thuật, tôi đã thấy hai cái được sử dụng để biểu thị những thứ hơi khác nhau.

Đối với một mẫu nên chấp nhận bất kỳ loại nào như T, bao gồm các phần dựng sẵn (chẳng hạn như một mảng)

template<typename T>
class Foo { ... }

Đối với một mẫu sẽ chỉ hoạt động trong đó T là một lớp thực sự.

template<class T>
class Foo { ... }

Nhưng hãy nhớ rằng đây hoàn toàn là một phong cách mà một số người sử dụng. Không bắt buộc bởi tiêu chuẩn hoặc được thực thi bởi trình biên dịch


15
Tôi không trách bạn đã đề cập đến nó, nhưng tôi nghĩ chính sách này khá sai lầm, vì các lập trình viên cuối cùng đã dành thời gian suy nghĩ về điều gì đó không quan trọng ("tôi đã sử dụng đúng chưa?") Để chỉ ra điều gì đó không ' t vấn đề ("có tồn tại loại tích hợp thực hiện giao diện cần thiết của tham số mẫu này không?"). Nếu bất kỳ thành viên nào của tham số mẫu được sử dụng ( T t; int i = t.toInt();) thì bạn cần một "lớp thực" và mã của bạn sẽ không được biên dịch nếu bạn cung cấp intcho T...
Steve Jessop

1
Nếu bạn muốn giới hạn việc sử dụng cho các lớp thực tế, tốt hơn hết bạn nên thêm một chuyên ngành để ném / gây ra lỗi cho các loại không phải lớp. Nếu bạn muốn giới hạn sử dụng cho các lớp cụ thể, chỉ chuyên cho chúng. Trong mọi trường hợp, một sự phân biệt phong cách như vậy là quá tinh tế để có được thông điệp trên.
Potatoswatter

2
Vì chúng có nghĩa tương tự, xin vui lòng chỉ sử dụng một. Mặt khác, nó giống như sử dụng nội tuyến {, trừ khi đó là thứ ba, và sau đó bạn sử dụng dòng tiếp theo {.
Paul Draper

+1 Đôi khi tôi tự làm điều này ... classngụ ý rằng bạn không chỉ mong đợi một "giá trị" có thể hỗ trợ một số nhà khai thác, sao chép hoặc di chuyển xây dựng và / hoặc chuyển nhượng, mà đặc biệt cần một loại hỗ trợ một số ngữ nghĩa truy cập thành viên. Nhìn nhanh nhất vào tuyên bố sau đó đặt kỳ vọng và không khuyến khích, ví dụ như cung cấp các loại tích hợp sẵn cho classcác tham số khi đó chắc chắn là một lỗi.
Tony Delroy

Tôi muốn hiểu những loại tình huống trong thế giới thực tồn tại trong đó một mẫu sẽ hoạt động cho BẤT K class lớp nào, nhưng sẽ không hoạt động với các loại tích hợp. Bạn có một ví dụ?
lfalin

7
  1. Không khác nhau
  2. Tham số loại mẫu Containertự nó là một mẫu có hai tham số loại.

3
có một sự khác biệt nói chung.
Hassan Syed

hai tham số mà container có thể được đặt tên cũng có thể được đặt tên? trong ví dụ họ không có bất kỳ tên nào. Và ngoài ra - trong ví dụ này, nó được viết là 'Class Container' - cũng có thể được viết 'typename Container' chứ?
Mat

2
@Mat: có, thuật ngữ để tìm kiếm là các tham số / đối số mẫu của mẫu . Ví dụ:template<template<class U> class V> struct C {};
Georg Fritzsche

6

Đoạn trích này là từ cuốn sách mồi c ++. Mặc dù tôi chắc chắn điều này là sai.

Mỗi tham số loại phải được bắt đầu bởi lớp từ khóa hoặc tên kiểu chữ:

// error: must precede U with either typename or class
template <typename T, U> T calc(const T&, const U&);

Các từ khóa này có cùng ý nghĩa và có thể được sử dụng thay thế cho nhau trong danh sách tham số mẫu. Một danh sách tham số mẫu có thể sử dụng cả hai từ khóa:

// ok: no distinction between typename and class in a template parameter list
template <typename T, class U> calc (const T&, const U&);

Có vẻ trực quan hơn khi sử dụng tên kiểu từ khóa thay vì lớp để chỉ định tham số loại mẫu. Rốt cuộc, chúng ta có thể sử dụng các kiểu dựng sẵn (không phải lớp) làm đối số kiểu mẫu. Hơn nữa, tên chữ rõ ràng hơn chỉ ra rằng tên theo sau là một loại tên. Tuy nhiên, tên chữ đã được thêm vào C ++ sau khi các mẫu đã được sử dụng rộng rãi; một số lập trình viên tiếp tục sử dụng lớp riêng

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.