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;
};

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.


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.


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


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.

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.