Nome do atributo confuso em C ++ para o modelo de membro
Descobri que ao acessar um atributo não modelo (v.foo
) de uma variável de um tipo de modelo (T& v
), C ++ pode ser induzido a pensar que é um modelo de membro se houver uma função de modelo com o mesmo nome (template class <T> void foo()
). Como isso pode ser explicado a partir da especificação do C ++? Considere este programa simples:
#include <cassert>
/** Determine whether the 'foo' attribute of an object is negative. */
template <class T>
bool foo_negative(T& v)
{
return v.foo < 0;
}
struct X
{
int foo;
};
int main()
{
X x;
x.foo = 5;
assert(!foo_negative(x));
return 0;
}
Nós temos uma função de modelofoo_negative
que toma um objeto de qualquer tipo e determina se seu atributo foo é negativo. omain
função instanciafoo_negative
com [T = X]. Este programa compila e executa sem qualquer saída.
Agora, adicione essa função ao topo do programa:
template <class T>
void foo()
{
}
Compilando-o com o G ++ 4.6.3 resulta neste erro do compilador:
funcs.cpp: In function ‘bool foo_negative(T&)’:
funcs.cpp:13:14: error: parse error in template argument list
funcs.cpp: In function ‘bool foo_negative(T&) [with T = X]’:
funcs.cpp:25:5: instantiated from here
funcs.cpp:13:14: error: ‘foo’ is not a member template function
(Onde a linha 13 éreturn v.foo < 0
e a linha 25 éassert(!foo_negative(x))
.)
Clang produz erros semelhantes.
Wat? Como a adição de uma função não relacionada que nunca é chamada gerencia introduzir um erro de sintaxe em um programa válido? Ao analisarfoo_negative
, o compilador não sabe o tipo dev
e, crucialmente, não sabe sev.foo
é um modelo de membro ou um membro regular. Aparentemente, ele tem que decidir no momento da análise (antes do modelo ser instanciado) se deve tratá-lo como um modelo de membro ou um membro regular.
Se pensav.foo
é um modelo de membro, então< 0
é visto como passando0
como um argumento de modelo, e há uma falta>
, daí o erro de sintaxe. Então quandofoo_negative
é instanciado com [T = X], há outro erro porqueX::foo
não é um modelo de membro.
Mas por que pensav.foo
é um modelo de membro? Essa ambiguidade é precisamente o quetemplate
palavra-chave é para: se eu escreviv.template foo
, então eu estaria explicitamente dizendo C ++ para esperar um modelo de membro, mas eu não usei otemplate
palavra chave! Eu não me referi a um modelo de membro, por isso deve assumir que é um membro regular. O fato de que há umfunção do mesmo nome que o membro não deve ter qualquer efeito. Por que isso? Não pode ser um bug no compilador porque o GCC e o clang são consistentes.