Pergunta

Eu estou tentando criar uma função:

template <typename T>
void doIt( T*& p )
{
   if ( !p ) { return; }
   T& ref = *p;
   getClassName( ref );
}

onde o comportamento varia de acordo com o tipo de p passado. Em particular, a versão do getClassName chamada deve depender do tipo de p. No exemplo a seguir, eu posso com sucesso ligue para:

doIt<myClass1>( myClass1*& )
doIt<myClass1<int> >( myClass1*& )
doIt<myClass2>( myClass2*& )
doIt<myClass2<int> >( myClass2*& )

mas falha quando eu chamo:

doIt< std::vector<int, std::allocator<int> > >( std::vector<int, std::allocator<int>>*& )

com o erro:

a.cxx: In function ‘void doIt(T*&) [with T = std::vector<int, std::allocator<int> >]’:
ba.cxx:87:   instantiated from here
a.cxx:33: error: invalid initialization of reference of type ‘MyClass1&’ from expression of type ‘std::vector<int, std::allocator<int> >’
a.cxx:16: error: in passing argument 1 of ‘const char* getClassName(MyClass1&)’

(gcc 4.2.4).

Se eu mover a declaração de:

template<typename T, typename A>
char const* getClassName( std::vector<T,A>& ) { printf("std::vector<T,A>\n"); return NULL; }

Antes de doit - então ele compila. Então,

  • Por que é necessário que aparece getClassName( std::vector<T,A>& ) antes doIt mas não getClassName( MyClass2T<T>& )
  • O que posso fazer para tornar doIt independente de std::vector? (Eu quero ser capaz de colocar doIt em seu próprio cabeçalho e não tem que saber sobre std::vector, ou qualquer uma das especializações, que será definido pelo usuário).

.

#include <stdio.h>
#include <assert.h>
#include <vector>

//template<typename T>
//char const* getClassName( T& );

//template<typename T, typename A>
////char const* getClassName( std::vector<T,A>& ) { printf("std::vector<T,A>\n"); return NULL; }

#if 1
// ---------  MyClass2
struct MyClass1
{};

char const* getClassName( MyClass1& ) { printf("MyClass1\n"); return NULL; }

// ---------  MyClass1T
template< typename T>
struct MyClass1T
{};

template<typename T>
char const* getClassName( MyClass1T<T>& ) { printf("MyClass1T<T>\n"); return NULL; }
#endif


template <typename T>
void doIt( T*& p )
{
   if ( !p ) { return; }
   T& ref = *p;
   getClassName( ref );
}


// ---------  MyClass2
struct MyClass2
{};


// declared after doIt, OK.
char const* getClassName( MyClass2& ) { printf("MyClass2\n"); return NULL; }

// ---------  MyClass2T
template< typename T>
struct MyClass2T
{};

// declared after doIt, OK.
template<typename T>
char const* getClassName( MyClass2T<T>& ) { printf("MyClass2T<T>\n"); return NULL; }

template<typename T, typename A>
char const* getClassName( std::vector<T,A>& ) { printf("std::vector<T,A>\n"); return NULL; }



void test()
{
#if 1
   MyClass1 mc1;
   MyClass1* mc1p = &mc1;
   doIt( mc1p );

   MyClass2 mc2;
   MyClass2* mc2p = &mc2;
   doIt( mc2p );

   MyClass1T<int> mc1t;
   MyClass1T<int>* mc1tp = &mc1t;
   doIt( mc1tp );

   MyClass2T<int> mc2t;
   MyClass2T<int>* mc2tp = &mc2t;
   doIt( mc2tp );

   // Nested templates are OK.
   MyClass2T<MyClass1> mc2t2;
   MyClass2T<MyClass1>* mc2tp2 = &mc2t2;
   doIt( mc2tp2 );
#endif

#if 1
   std::vector<int> v;
   std::vector<int>* vp = &v;
   doIt( vp );                   // FAIL!
#endif
}
Foi útil?

Solução

Por que é necessário que GetClassName (std :: vector &) aparecem antes doit, mas não GetClassName (MyClass2T &)

Uma declaração no escopo é necessário para qualquer função. Quando você criar a sua função de modelo com um vector<int> que espera uma função com o getClassName(vector<int>&) assinatura de estar presente (pelo menos um protótipo) para compilação para ter sucesso.

O que posso faço make doit independente de std :: vector? (Eu quero ser capaz de colocar doit no seu próprio cabeçalho e não tem que saber sobre std :: vector, ou qualquer uma das especializações, que será definido pelo usuário)

Leia a FAQ sobre Modelos . Tente colocar o protótipo de todas as funções do modelo dependentes do doIt antes da primeira instanciação de doIt.

Outras dicas

A razão para a falha é que a instanciação, nenhuma pesquisa de nome não qualificado para as funções ocorrer (mas apenas ADL - Argumento Lookup Dependente). A instanciação contexto é (feita a partir de 14.6.4.1/6 o padrão C ++):

A instanciação contexto de uma expressão que depende dos argumentos de modelo é o conjunto de declarações com ligação externa declarados antes do ponto de instanciação do modelo de especialização na mesma unidade de tradução.

O ponto de instanciação de todas essas especializações modelo vos chamou, neste caso, é apenas após a definição de test (14.6.4.1/1 leitura). Assim, todas as funções que você declarados são visíveis em sua função test usando pesquisa não qualificado, mas a pesquisa para eles é realmente diferente para as chamadas de função:

A chamada de função que depende de um parâmetro de modelo dentro de um modelo é procurado como esta:

  • Nomes do contexto de definição de modelo são consideradas por ambos pesquisa comum e ADL.
  • Nomes de contexto instanciação são considerados apenas para ADL.

Isto significa que, porque não há nenhuma função getClassName adequado declarou no contexto definição do modelo, uma função adequada tem de ser encontrada no contexto instanciação usando ADL - caso contrário, a chamada irá falhar e não encontrar qualquer declaração.

Argumento Dependente Lookup (ADL)

Para um argumento do tipo std::vector<T>, pesquisas ADL para funções em std namespace eo namespace de T. Colocando a função getClassName no namespace std iria trabalhar para isso (mas isso não é permitido pela norma, pois que os rendimentos para um comportamento indefinido - isto deve ser feito apenas como último recurso).

Para ver os efeitos da tentativa ADL para doIt chamada com um vetor de MyClass2 vez de int. Desde então T = MyClass2, ADL irá procurar no espaço de nomes de MyClass2 para uma função adequada de aceitar um std::vector<MyClass2> e terá sucesso - contrário de quando você usa int, que só vai olhar para std.

Para as outras chamadas de função, as respectivas declarações são encontrados também, porque todos eles são declarados no namespace global, em que os tipos de argumentos das chamadas de função são definidos também (MyClass1, MyClass2 etc).

A ++ FAQ C é bom, mas não ir fundo em templates (não encontrei nenhuma menção de ADL nele). Há um dedicado modelo FAQ que lida com algumas das armadilhas mais intrincados .


Cuidado com um comportamento indefinido

Note que muitos compiladores aceitará o código, mesmo quando você colocar essa declaração i mostrou após a função test (em vez de antes). Mas como o acima Citação padrão diz, em seguida, a declaração não será parte do contexto instanciação e do Estado encontrada em 14.6.4.2/1 é para ser visto:

Se a chamada seria mal-formado ou iria encontrar um jogo melhor tinha a pesquisa dentro dos namespaces associados consideradas todas as declarações de função com ligação externa introduzido nos espaços de nomes em todas as unidades de tradução, não apenas considerando essas declarações encontrada no modelo definição e instanciação de modelo contextos, em seguida, o programa tem um comportamento indefinido.

Assim, o que parece trabalho seria um comportamento indefinido. É válido para um compilador para aceitá-lo, mas é de igual modo válido por um rejeitá-la ou para falhar e terminar. Então preste atenção que qualquer nome necessário é realmente visível no contexto instanciação como explicado.

Espero que isso ajude.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top