Estou brincando com o clang há algum tempo e me deparei com "test / SemaTemplate / dependente-template-recover.cpp" (na distribuição do clang), que supostamente fornece dicas para se recuperar de um erro de modelo.
A coisa toda pode ser facilmente reduzida a um exemplo mínimo:
template<typename T, typename U, int N> struct X {
void f(T* t)
{
// expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}}
t->f0<U>();
}
};
A mensagem de erro gerada pelo clang:
tpl.cpp:6:13: error: use 'template' keyword to treat 'f0' as a dependent template name
t->f0<U>();
^
template
1 error generated.
... Mas tenho dificuldade em entender onde exatamente se deve inserir a template
palavra-chave para que o código seja sintaticamente correto?
Respostas:
ISO C ++ 03 14.2 / 4:
In
t->f0<U>();
f0<U>
é uma especialização de modelo de membro que aparece depois->
e que depende explicitamente do parâmetro do modeloU
, portanto, a especialização de modelo de membro deve ser prefixada portemplate
palavra-chave.Portanto, mude
t->f0<U>()
parat->template f0<U>()
.fonte
t->(f0<U>())
teria consertado isso, pois pensei que colocariaf0<U>()
em expressão autônoma ... bem, pensei errado, ao que parece ...Além dos pontos que outros fizeram, observe que às vezes o compilador não conseguia se decidir e ambas as interpretações podem gerar programas alternativos válidos ao instanciar
#include <iostream> template<typename T> struct A { typedef int R(); template<typename U> static U *f(int) { return 0; } static int f() { return 0; } }; template<typename T> bool g() { A<T> a; return !(typename A<T>::R*)a.f<int()>(0); } int main() { std::cout << g<void>() << std::endl; }
Imprime
0
ao omitirtemplate
antes,f<int()>
mas1
ao inseri-lo. Deixo isso como um exercício para descobrir o que o código faz.fonte
f<U>
e sempre imprime1
, o que faz todo o sentido para mim. Ainda não entendo por que atemplate
palavra-chave é necessária e que diferença faz.template
é necessário: stackoverflow.com/questions/610245/… sem depender apenas dos termos padrão que são difíceis de entender. Relate se alguma coisa nessa resposta ainda estiver confusa.Insira-o imediatamente antes do ponto onde o cursor é:
template<typename T, typename U, int N> struct X { void f(T* t) { t->template f0<U>(); } };
Editar: o motivo desta regra fica mais claro se você pensar como um compilador.
Os compiladores geralmente olham apenas um ou dois tokens de uma vez e geralmente não "olham para frente" para o resto da expressão.[Editar: ver comentário] O motivo da palavra-chave é o mesmo por que você precisa datypename
palavra-chave para indicar nomes de tipo dependente: está dizendo ao compilador "ei, o identificador que você está prestes a ver é o nome de um modelo, em vez de o nome de um membro de dados estáticos seguido por um sinal de menor que ".fonte
template
. Há casos em que com e semtemplate
produzirão programas válidos com comportamento diferente. Portanto, este não é apenas um problema sintático (t->f0<int()>(0)
é válido sintaticamente para a versão da lista de argumentos menor que e do modelo).Trecho de modelos C ++
A construção .template Um problema muito semelhante foi descoberto após a introdução de typename. Considere o seguinte exemplo usando o tipo de bitset padrão:
template<int N> void printBitset (std::bitset<N> const& bs) { std::cout << bs.template to_string<char,char_traits<char>, allocator<char> >(); }
A construção estranha neste exemplo é .template. Sem esse uso extra do modelo, o compilador não sabe que o token menor que (<) que se segue não é realmente "menor que", mas o início de uma lista de argumentos do modelo. Observe que isso é um problema apenas se a construção antes do período depender de um parâmetro de modelo. Em nosso exemplo, o parâmetro bs depende do parâmetro de modelo N.
Em conclusão, a notação .template (e notações semelhantes como -> template) deve ser usada apenas dentro de templates e somente se eles seguirem algo que dependa de um parâmetro de template.
fonte