Perbedaan kata kunci 'typename' dan 'class' dalam templat?

504

Untuk templat saya telah melihat kedua deklarasi:

template < typename T >
template < class T >

Apa bedanya?

Dan apa sebenarnya arti kata kunci tersebut dalam contoh berikut (diambil dari artikel Wikipedia bahasa Jerman tentang templat)?

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

Jawaban:

430

typenamedan classdapat dipertukarkan dalam kasus dasar menentukan templat:

template<class T>
class Foo
{
};

dan

template<typename T>
class Foo
{
};

adalah setara.

Karena itu, ada kasus khusus di mana ada perbedaan antara typenamedan class.

Yang pertama adalah dalam kasus tipe dependen. typenamedigunakan untuk mendeklarasikan saat Anda mereferensikan tipe bersarang yang bergantung pada parameter templat lain, seperti typedefdalam contoh ini:

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

Pertanyaan kedua yang sebenarnya Anda tunjukkan dalam pertanyaan Anda, meskipun Anda mungkin tidak menyadarinya:

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

Saat menentukan templat templat , classkata kunci HARUS digunakan seperti di atas - tidak dapat dipertukarkan dengan typenamekasus ini (catatan: karena C ++ 17 kedua kata kunci diizinkan dalam kasus ini) .

Anda juga harus menggunakan classketika secara eksplisit membuat template:

template class Foo<int>;

Saya yakin ada beberapa kasus lain yang saya lewatkan, tetapi intinya adalah: dua kata kunci ini tidak setara, dan ini adalah beberapa kasus umum di mana Anda harus menggunakan satu atau yang lain.

Aaron Klotz
sumber
45
Yang terakhir cukup banyak kasus khusus dari fakta bahwa Anda harus menggunakan kelas atau struct, bukan tipe nama, untuk mendefinisikan kelas. Jelas tak satu pun dari dua bit kode Anda yang pertama dapat diganti template <typename T> typename Foo {};, karena Foo <T> jelas merupakan kelas.
Steve Jessop
2
std::vector<int>::value_typebukan tipe dependen, Anda tidak perlu di typenamesana - Anda hanya memerlukannya jika tipe bergantung pada parameter templat, katakanlahtemplate<class T> struct C { typedef typename std::vector<T>::value_type type; };
Georg Fritzsche
2
Dan lagi, param_tbukan tipe dependen. Tipe dependen adalah nama yang bergantung pada parameter templat , misalnya foo<param_t>::some_type, bukan parameter templat itu sendiri.
Georg Fritzsche
2
Proposal C ++ 1z N4051 akan memungkinkan Anda untuk menggunakan typename, yaitu template <typename> typename C.
user4112979
4
Pada GCC 5, G ++ sekarang memungkinkan nama ketik dalam parameter templat templat .
Chnossos
95

Untuk parameter penamaan template, typenamedan classsetara. §14.1.2:

Tidak ada perbedaan semantik antara kelas dan nama ketik di parameter-templat.

typenamenamun dimungkinkan dalam konteks lain saat menggunakan template - untuk memberi petunjuk pada kompiler bahwa Anda merujuk pada tipe dependen. §14.6.2:

Nama yang digunakan dalam pernyataan atau definisi templat dan yang bergantung pada parameter templat diasumsikan tidak menyebutkan nama jenis kecuali pencarian nama yang berlaku menemukan nama jenis atau nama tersebut dikualifikasikan oleh nama kunci kata kunci.

Contoh:

typename some_template<T>::some_type

Tanpa typenamekompiler tidak dapat memberi tahu secara umum apakah Anda merujuk pada suatu tipe atau tidak.

Georg Fritzsche
sumber
2
Saya mengerti aturannya, tapi apa sebenarnya yang mencegah kompiler memperlakukan some_template <T> sebagai tipe internal? Maaf jika saya kehilangan sesuatu yang jelas.
batbrat
23

Meskipun tidak ada perbedaan teknis, saya telah melihat keduanya digunakan untuk menunjukkan hal-hal yang sedikit berbeda.

Untuk templat yang menerima jenis apa pun sebagai T, termasuk bawaan (seperti larik)

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

Untuk templat yang hanya akan berfungsi jika T adalah kelas nyata.

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

Namun perlu diingat bahwa ini adalah murni gaya yang digunakan sebagian orang. Tidak diamanatkan oleh standar atau dipaksakan oleh kompiler

Michael Anderson
sumber
15
Saya tidak menyalahkan Anda karena menyebutkannya, tetapi saya pikir kebijakan ini cukup keliru, karena pemrogram akhirnya mengambil waktu untuk memikirkan sesuatu yang tidak penting ("sudahkah saya menggunakan yang benar?") Untuk menunjukkan sesuatu yang tidak t masalah ("apakah ada tipe bawaan yang mengimplementasikan antarmuka yang diperlukan dari parameter templat ini?"). Jika ada anggota parameter templat yang digunakan ( T t; int i = t.toInt();) maka Anda memerlukan "kelas nyata", dan kode Anda tidak dapat dikompilasi jika Anda menyediakan intuntuk T...
Steve Jessop
1
Jika Anda ingin membatasi penggunaan untuk kelas yang sebenarnya, Anda lebih baik menambahkan spesialisasi untuk melempar / menyebabkan kesalahan untuk jenis non-kelas. Jika Anda ingin membatasi penggunaan untuk kelas-kelas tertentu, hanya khususkan untuk mereka. Bagaimanapun, perbedaan gaya seperti itu terlalu halus untuk menyampaikan pesan.
Potatoswatter
2
Karena mereka memiliki arti yang sama, silakan gunakan hanya satu. Kalau tidak, itu seperti menggunakan inline {, kecuali hari Selasa, dan kemudian Anda menggunakan next-line {.
Paul Draper
+1 Saya melakukan ini sendiri kadang-kadang ... classmenyiratkan bahwa Anda tidak hanya mengharapkan "nilai" yang mungkin mendukung beberapa operator, menyalin atau memindahkan konstruksi dan / atau penugasan, tetapi secara khusus membutuhkan jenis yang mendukung beberapa semantik akses anggota. Pandangan tercepat pada deklarasi kemudian menetapkan harapan dan patah semangat misalnya memasok tipe classbawaan untuk parameter ketika itu pasti akan menjadi kesalahan.
Tony Delroy
Saya ingin memahami jenis situasi dunia nyata yang ada di mana templat akan bekerja untuk kelas APAPUN, tetapi tidak akan berfungsi dengan tipe bawaan. Apakah kamu punya contoh?
lfalin
7
  1. Tidak ada perbedaan
  2. Parameter tipe templat Containersendiri merupakan templat dengan dua jenis parameter.
Nikolai Fetissov
sumber
3
ada perbedaan secara umum.
Hassan Syed
mungkinkah kedua parameter wadah itu templated dengan juga disebutkan? dalam contoh mereka tidak memiliki nama. Dan juga - dalam contoh ini ditulis 'Container kelas' - dapatkah ada juga yang dituliskan 'typename Container'?
Mat
2
@ Mat: ya, istilah yang dicari adalah parameter / argumen templat templat . Misalnya:template<template<class U> class V> struct C {};
Georg Fritzsche
6

Cuplikan ini dari c ++ buku utama. Meskipun saya yakin ini salah.

Setiap parameter tipe harus didahului oleh kelas kata kunci atau nama ketik:

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

Kata kunci ini memiliki arti yang sama dan dapat digunakan secara bergantian di dalam daftar parameter template. Daftar parameter templat dapat menggunakan kedua kata kunci:

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

Tampaknya lebih intuitif untuk menggunakan nama kunci kata kunci daripada kelas untuk menetapkan parameter jenis template. Lagi pula, kita bisa menggunakan tipe bawaan (bukan kelas) sebagai argumen tipe templat. Selain itu, nama ketik lebih jelas menunjukkan bahwa nama yang mengikuti adalah nama jenis. Namun, nama ketik ditambahkan ke C ++ setelah templat sudah digunakan secara luas; beberapa programmer terus menggunakan kelas secara eksklusif

KK
sumber