Várias definições de um modelo de função
Pergunta
Suponha que um arquivo de cabeçalho define um modelo de função. Agora, dois arquivos de implementação suponha #include
este cabeçalho, e cada um deles tem uma chamada para o modelo de função. Em ambos implementação arquiva o modelo de função é instanciado com o mesmo tipo.
// header.hh
template <typename T>
void f(const T& o)
{
// ...
}
// impl1.cc
#include "header.hh"
void fimpl1()
{
f(42);
}
// impl2.cc
#include "header.hh"
void fimpl2()
{
f(24);
}
Pode-se esperar que o vinculador iria reclamar sobre várias definições de f()
. Especificamente, se f()
não seria um modelo, então, que seria de fato o caso.
- Como é que o vinculador não reclamar várias definições de
f()
? - É especificado no padrão que o vinculador deve lidar com esta situação graciosamente? Em outras palavras, eu posso sempre contar com programas similares ao acima para compilar e link?
- Se o ligador pode ser o suficiente inteligente para disambiguate um conjunto de funções instâncias de modelo, porque ele não pode fazer o mesmo para funções regulares, uma vez que eles são idênticos, como é o caso de modelos de função instanciado?
Solução
A fim de apoiar C ++, o vinculador é inteligente o suficiente para reconhecer que eles são todos a mesma função e joga fora todos menos um.
EDIT: Esclarecimento: O vinculador não comparar conteúdos de função e determinar que eles são os mesmos. funções Templated são marcadas como tal e o vinculador reconhece que eles têm as mesmas assinaturas.
Outras dicas
manual do compilador GNU C ++ tem uma boa discussão deste . Um trecho:
modelos C ++ são a primeira língua apresentam a exigir mais inteligência a partir do ambiente do que um normalmente encontra em um sistema UNIX. De alguma forma o compilador e vinculador tem que ter certeza que cada instância do molde ocorre exatamente uma vez no executável se é necessário, e não em tudo o contrário. Existem duas abordagens básicas para esta problema, que são referidos como a Borland modelo eo modelo Cfront.
modelo Borland
Borland C ++ resolvido o modelo problema instanciação adicionando o código equivalente de blocos comuns aos sua vinculador; os emite compilador exemplos de modelo em cada tradução unidade que usa-los, e o ligador desmorona-los juntos. A vantagem deste modelo é que o vinculador única tem de considerar os arquivos objeto si mesmos; não há externa complexidade com que se preocupar. este desvantagem é que o tempo de compilação é aumentada porque o código do modelo está sendo compilado repetidamente. Código escrito para este modelo tende a incluem definições de todos os modelos no cabeçalho do arquivo, uma vez que deve ser visto para ser instanciado.
Cfront modelo
A AT & T C ++ tradutor, Cfront, resolveu o instanciação de modelo problema criando a noção de um repositório de modelos, um automaticamente lugar mantido onde template casos são armazenados. A mais moderna versão do repositório funciona como seguinte maneira: Como arquivos de objetos individuais são construídos, o compilador coloca qualquer definições de modelo e instantiations encontradas no repositório. Em tempo de link, o link envoltório acrescenta os objetos no repositório e compila qualquer necessária instâncias que não eram previamente emitida. As vantagens deste modelo são mais velocidade óptima e compilação a capacidade para utilizar o sistema de ligante; para implementar o Borland modelo de um fornecedor compilador também precisa de substituir vinculador. As desvantagens são muito maior complexidade, e assim potencial de erro; para algum código isso pode ser tão transparente, mas na prática, pode ser muito difícil a programas de construção múltiplas em um diretório e um programa em múltiplos diretórios. O código escrito para este modelo tende a definições separadas de modelos de membros não-incorporadas em um arquivo separado, que deve ser compilados separadamente.
Quando usado com GNU versão 2.8 ou ld mais tarde em um sistema ELF como GNU / Linux ou Solaris 2, ou em Microsoft Windows, G ++ suporta a modelo Borland. Em outros sistemas, G ++ implementos nem modelo automático.
Este é mais ou menos um caso especial apenas para modelos.
O compilador só gera as instâncias de modelo que são realmente utilizados. Uma vez que não tem controle sobre o que o código será gerado a partir de outros arquivos de origem, tem que gerar o código do modelo, uma vez para cada arquivo, para se certificar de que o método é gerada em tudo.
Uma vez que é difícil de resolver isso (o padrão tem uma palavra-chave extern
para modelos, mas g ++ não implementá-lo) o ligador simplesmente aceita as várias definições.