Różnica słów kluczowych „nazwa typu” i „klasa” w szablonach?


504

W przypadku szablonów widziałem obie deklaracje:

template < typename T >
template < class T >

Co za różnica?

A co dokładnie oznaczają te słowa kluczowe w poniższym przykładzie (zaczerpniętym z niemieckiego artykułu w Wikipedii na temat szablonów)?

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

Odpowiedzi:


430

typenamei classsą wymienne w podstawowym przypadku określania szablonu:

template<class T>
class Foo
{
};

i

template<typename T>
class Foo
{
};

są równoważne.

To powiedziawszy, istnieją szczególne przypadki, w których istnieje różnica między typenameiclass .

Pierwszy dotyczy typów zależnych. typenamesłuży do deklarowania, gdy odwołujesz się do typu zagnieżdżonego, który zależy od innego parametru szablonu, takiego jak typedefw tym przykładzie:

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

Drugi, który faktycznie pokazujesz w swoim pytaniu, choć możesz nie zdawać sobie z tego sprawy:

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

Określając szablon szablon The classkluczowe muszą być używane jak wyżej - to nie zamiennie ze typenamew tym przypadku (uwaga: od C ++ 17 oba słowa kluczowe są dozwolone w tym przypadku) .

Musisz także użyć classpodczas jawnego tworzenia szablonu:

template class Foo<int>;

Jestem pewien, że są inne przypadki, które przegapiłem, ale sedno jest następujące: te dwa słowa kluczowe nie są równoważne i są to niektóre typowe przypadki, w których musisz użyć jednego lub drugiego.


45
Ten ostatni jest szczególnym przypadkiem tego, że do zdefiniowania klasy musisz użyć klasy lub struktury, a nie nazwy typu. Oczywiście żaden z pierwszych dwóch bitów kodu nie może zostać zastąpiony template <typename T> typename Foo {};, ponieważ Foo <T> jest zdecydowanie klasą.
Steve Jessop

2
std::vector<int>::value_typenie jest typem zależnym, nie potrzebujesz go typenametam - potrzebujesz go tylko, jeśli typ zależy od parametru szablonu, powiedzmytemplate<class T> struct C { typedef typename std::vector<T>::value_type type; };
Georg Fritzsche

2
I znowu, param_tnie jest typem zależnym. Typy zależne to nazwy zależne od parametru szablonu , np foo<param_t>::some_type. Same parametry szablonu.
Georg Fritzsche

2
Propozycja C ++ 1z N4051 pozwoli ci na użycie typename, tj template <typename> typename C.
user4112979,

4
Na dzień GCC 5, G ++ pozwala teraz TypeName w parametrze szablon szablon .
Chnossos,

95

Do nazewnictwa parametrów szablonu typenamei classsą one równoważne. §14.1.2:

W parametrze szablonu nie ma semantycznej różnicy między klasą a nazwą typu.

typenamejest to jednak możliwe w innym kontekście podczas korzystania z szablonów - aby wskazać kompilatorowi, że masz na myśli typ zależny. 14.6.2:

Zakłada się, że nazwa używana w deklaracji lub definicji szablonu, która jest zależna od parametru szablonu, nie nadaje nazwy typowi, chyba że odpowiednie wyszukiwanie nazwy znajdzie nazwę typu lub nazwa kwalifikowana jest przez słowo kluczowe typename.

Przykład:

typename some_template<T>::some_type

Bez typenamekompilatora nie można ogólnie stwierdzić, czy masz na myśli typ, czy nie.


2
Rozumiem tę zasadę, ale co dokładnie uniemożliwia kompilatorowi traktowanie some_template <T> jako typu wewnętrznie? Przepraszam, jeśli brakuje mi czegoś oczywistego.
batbrat

23

Chociaż nie ma żadnej różnicy technicznej, widziałem, że oba oznaczały nieco inne rzeczy.

Dla szablonu, który powinien akceptować dowolny typ jako T, w tym wbudowane (takie jak tablica)

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

Dla szablonu, który będzie działał tylko tam, gdzie T jest prawdziwą klasą.

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

Pamiętaj jednak, że niektórzy ludzie używają tego stylu. Nie jest to wymagane przez standard lub wymuszone przez kompilatory


15
Nie obwiniam cię za to, że o tym wspomniałeś, ale myślę, że ta polityka jest dość myląca, ponieważ programiści poświęcają czas na myślenie o czymś, co nie ma znaczenia („czy użyłem właściwego?”), Aby wskazać coś, co nie ma to znaczenie („czy istnieje wbudowany typ, który implementuje interfejs wymagany dla tego parametru szablonu?”). Jeśli używany jest jakikolwiek element parametru szablonu ( T t; int i = t.toInt();), potrzebujesz „prawdziwej klasy”, a twój kod nie będzie się kompilował, jeśli dostarczysz intdla T...
Steve Jessop

1
Jeśli chcesz ograniczyć użycie do rzeczywistych klas, lepiej jest dodać specjalizację, aby rzucić / spowodować błąd dla typów nieklasowych. Jeśli chcesz ograniczyć użycie do określonych klas, specjalizuj się tylko dla nich. W każdym razie takie rozróżnienie stylistyczne jest zbyt subtelne, aby przekazać przekaz.
Potatoswatter

2
Ponieważ oznaczały to samo, użyj tylko jednego. W przeciwnym razie jest to jak użycie inline {, chyba że jest wtorek, a następnie użycie następnej linii {.
Paul Draper,

+1 Czasami robię to sam ... classoznacza, że ​​nie tylko oczekujesz „wartości”, być może wspierającej niektórych operatorów, kopiującej lub przenoszącej konstrukcję i / lub przypisanie, ale konkretnie potrzebujesz typu obsługującego niektóre semantyki dostępu do elementu. Najszybsze spojrzenie na deklarację określa następnie oczekiwania i odradza np. Dostarczanie wbudowanych typów classparametrów, gdy z pewnością byłby to błąd.
Tony Delroy

Chciałbym zrozumieć, jakie rodzaje rzeczywistych sytuacji istnieją, w których szablon działałby dla KAŻDEJ klasy, ale nie działałby z wbudowanymi typami. Czy masz przykład?
lfalin

7
  1. Bez różnicy
  2. Parametr typu szablonu Containersam w sobie jest szablonem z dwoma parametrami typu.

3
jest ogólna różnica.
Hassan Syed

czy te dwa parametry, z którymi szablon jest utworzony, mogą być nazwane? w przykładzie nie mają żadnych nazw. A także - w tym przykładzie jest napisane „kontener klasy” - czy zamiast tego można również napisać „typename Container”?
Mat.

2
@Mata: tak, terminem do wyszukania są parametry / argumenty szablonu szablonu . Np .:template<template<class U> class V> struct C {};
Georg Fritzsche,

6

Ten fragment kodu pochodzi z książki z podstawami języka C ++. Chociaż jestem pewien, że to źle.

Każdy parametr typu musi być poprzedzony klasą słowa kluczowego lub nazwą typu:

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

Te słowa kluczowe mają to samo znaczenie i mogą być używane zamiennie w liście parametrów szablonu. Lista parametrów szablonu może zawierać oba słowa kluczowe:

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

Bardziej intuicyjne może być użycie słowa kluczowego nazwa_typu zamiast klasy do określenia parametru typu szablonu. W końcu możemy używać wbudowanych (nieklasowych) typów jako argumentów typu szablonu. Ponadto nazwa typu wyraźniej wskazuje, że następująca nazwa jest nazwą typu. Jednak nazwa typu została dodana do C ++ po tym, jak szablony były już w powszechnym użyciu; niektórzy programiści nadal używają wyłącznie klasy

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.