Diferença das palavras-chave 'typename' e 'class' nos modelos?

504

Para modelos, vi as duas declarações:

template < typename T >
template < class T >

Qual é a diferença?

E o que exatamente essas palavras-chave significam no exemplo a seguir (extraído do artigo alemão da Wikipedia sobre modelos)?

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

Respostas:

430

typenamee classsão intercambiáveis ​​no caso básico de especificar um modelo:

template<class T>
class Foo
{
};

e

template<typename T>
class Foo
{
};

são equivalentes.

Dito isto, há casos específicos em que há uma diferença entre typenamee class.

O primeiro é no caso de tipos dependentes. typenameé usado para declarar quando você está referenciando um tipo aninhado que depende de outro parâmetro de modelo, como typedefneste exemplo:

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

O segundo que você realmente mostra na sua pergunta, embora você possa não perceber:

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

Ao especificar um modelo modelo , a classpalavra-chave deve ser usada como acima - é não intercambiáveis com typenameneste caso (nota: uma vez C ++ 17 palavras-chave são permitidos neste caso) .

Você também deve usar classao instanciar explicitamente um modelo:

template class Foo<int>;

Tenho certeza de que há outros casos que perdi, mas o ponto principal é: essas duas palavras-chave não são equivalentes e são alguns casos comuns em que você precisa usar uma ou outra.

Aaron Klotz
fonte
45
Esse último é praticamente um caso especial do fato de que você deve usar classe ou estrutura, e não o nome do tipo, para definir uma classe. Obviamente, nenhum dos seus dois primeiros bits de código pode ser substituído template <typename T> typename Foo {};, porque Foo <T> é definitivamente uma classe.
Steve Jessop
2
std::vector<int>::value_typenão é um tipo dependente, você não precisa typenamedele - você só precisa se um tipo depende de um parâmetro do modelo, digamostemplate<class T> struct C { typedef typename std::vector<T>::value_type type; };
Georg Fritzsche
2
E, novamente, param_tnão é um tipo dependente. Tipos dependentes são nomes que dependem de um parâmetro do modelo , por exemplo foo<param_t>::some_type, não os próprios parâmetros do modelo.
Georg Fritzsche 26/01
2
Uma proposta C40 1z N4051 permitirá que você use typename, ou seja template <typename> typename C.
user4112979
4
A partir de agoraGCC 5 , o G ++ agora permite o nome do tipo em um parâmetro de modelo .
Chnossos 8/11
95

Para nomear parâmetros do modelo typenamee classsão equivalentes. §14.1.2:

Não há diferença semântica entre classe e typename em um parâmetro de modelo.

typenameno entanto, é possível em outro contexto ao usar modelos - para sugerir ao compilador que você está se referindo a um tipo dependente. §14.6.2:

Um nome usado em uma declaração ou definição de modelo e dependente de um parâmetro de modelo é assumido para não nomear um tipo, a menos que a pesquisa de nome aplicável encontre um nome de tipo ou o nome seja qualificado pela palavra-chave typename.

Exemplo:

typename some_template<T>::some_type

Sem typenameo compilador, não se pode dizer em geral se você está se referindo a um tipo ou não.

Georg Fritzsche
fonte
2
Eu entendo a regra, mas o que exatamente impede o compilador de tratar some_template <T> como um tipo internamente? Desculpe se estou perdendo algo óbvio.
batbrat
23

Embora não haja diferença técnica, vi os dois usados ​​para denotar coisas ligeiramente diferentes.

Para um modelo que deve aceitar qualquer tipo como T, incluindo embutidos (como uma matriz)

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

Para um modelo que funcionará apenas onde T é uma classe real.

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

Mas lembre-se de que isso é puramente algo que algumas pessoas usam. Não é obrigatório pelo padrão ou imposto pelos compiladores

Michael Anderson
fonte
15
Não o culpo por mencioná-lo, mas acho que essa política é muito equivocada, já que os programadores acabam demorando a pensar em algo que não importa ("usei o correto?") Para indicar algo que não importa. importa ("existe um tipo interno que implementa a interface necessária para este parâmetro de modelo?"). Se forem utilizados quaisquer membros do parâmetro do modelo ( T t; int i = t.toInt();), então você precisa de uma "classe real", e seu código não será compilado se você fornecer intpara T...
Steve Jessop
1
Se você deseja limitar o uso a classes reais, é melhor adicionar uma especialização para gerar / causar um erro para tipos que não são de classe. Se você deseja limitar o uso a classes específicas, especialize-se apenas para elas. De qualquer forma, essa distinção estilística é sutil demais para transmitir a mensagem.
Potatoswatter
2
Como eles significaram a mesma coisa, use apenas um. Caso contrário, é como usar inline {, a menos que seja terça-feira, e você usar a próxima linha {.
Paul Draper
Às vezes, eu mesmo faço isso algumas vezes ... classimplica que você não está apenas esperando um "valor", talvez apoiando alguns operadores, copie ou mova a construção e / ou atribuição, mas precise especificamente de um tipo que suporte alguma semântica de acesso de membro. Um rápido olhar para a declaração define expectativas e desencoraja, por exemplo, fornecer tipos classinternos para parâmetros quando isso certamente seria um erro.
Tony Delroy
Gostaria de entender que tipos de situações do mundo real existem onde um modelo funcionaria para QUALQUER classe, mas não funcionaria com tipos internos. Você tem um exemplo?
Lfalin 23/11/2015
7
  1. Sem diferença
  2. O parâmetro de tipo de modelo Containeré ele próprio um modelo com dois parâmetros de tipo.
Nikolai Fetissov
fonte
3
há uma diferença em geral.
Hassan Syed
esses dois parâmetros com o qual o contêiner é modelado também podem ser nomeados? no exemplo, eles não têm nomes. E também - neste exemplo está escrito 'class Container' - também poderia ser escrito 'typename Container'?
Mat
2
@ Mat: sim, o termo a ser pesquisado é parâmetros / argumentos do modelo . Exemplo:template<template<class U> class V> struct C {};
Georg Fritzsche
6

Este fragmento é do livro de iniciação c ++. Embora eu tenha certeza que isso está errado.

Cada parâmetro de tipo deve ser precedido pela classe de palavra-chave ou nome do tipo:

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

Essas palavras-chave têm o mesmo significado e podem ser usadas alternadamente dentro de uma lista de parâmetros do modelo. Uma lista de parâmetros do modelo pode usar as duas palavras-chave:

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

Pode parecer mais intuitivo usar a palavra-chave typename em vez de classe para designar um parâmetro de tipo de modelo. Afinal, podemos usar tipos internos (não classe) como um argumento de tipo de modelo. Além disso, o typename indica mais claramente que o nome a seguir é um nome de tipo. No entanto, o typename foi adicionado ao C ++ depois que os modelos já estavam em uso generalizado; alguns programadores continuam a usar a classe exclusivamente

KK
fonte