Figura 1: modelos de função
TemplHeader.h
template<typename T>
void f();
TemplCpp.cpp
template<typename T>
void f(){
//...
}
//explicit instantation
template void f<T>();
Main.cpp
#include "TemplHeader.h"
extern template void f<T>(); //is this correct?
int main() {
f<char>();
return 0;
}
Esta é a maneira correta de usar extern template
ou devo usar essa palavra-chave apenas para modelos de classe, como na Figura 2?
Figura 2: modelos de classe
TemplHeader.h
template<typename T>
class foo {
T f();
};
TemplCpp.cpp
template<typename T>
void foo<T>::f() {
//...
}
//explicit instantation
template class foo<int>;
Main.cpp
#include "TemplHeader.h"
extern template class foo<int>();
int main() {
foo<int> test;
return 0;
}
Eu sei que é bom colocar tudo isso em um arquivo de cabeçalho, mas se instanciarmos modelos com os mesmos parâmetros em vários arquivos, teremos várias definições iguais e o compilador removerá todos (exceto um) para evitar erros. Como faço para usar extern template
? Podemos usá-lo apenas para classes ou podemos usá-lo para funções também?
Além disso, a Figura 1 e a Figura 2 podem ser expandidas para uma solução em que os modelos estão em um único arquivo de cabeçalho. Nesse caso, precisamos usar a extern template
palavra-chave para evitar várias instanciações iguais. Isso é apenas para classes ou funções também?
extern template class foo<int>();
parece um erro.()
da linha externa. seu livro e estúdio visual estão errados, tente usar um compilador compatível com o padrão, como g ++ ou clang, e você verá o problema.Respostas:
Você só deve usar
extern template
para forçar o compilador a não instanciar um modelo quando souber que ele será instanciado em outro lugar. É usado para reduzir o tempo de compilação e o tamanho do arquivo de objeto.Por exemplo:
Isso resultará nos seguintes arquivos de objeto:
Se os dois arquivos estiverem vinculados, um
void ReallyBigFunction<int>()
será descartado, resultando em perda de tempo de compilação e tamanho do arquivo de objeto.Para não perder tempo de compilação e tamanho de arquivo de objeto, existe uma
extern
palavra - chave que faz com que o compilador não compile uma função de modelo. Você deve usar isso se e somente se souber que é usado no mesmo binário em outro lugar.Mudando
source2.cpp
para:Resultará nos seguintes arquivos de objeto:
Quando ambos estiverem vinculados, o segundo arquivo de objeto usará apenas o símbolo do primeiro arquivo de objeto. Não há necessidade de descarte, tempo de compilação e tamanho de arquivo de objeto desperdiçado
Isso deve ser usado apenas dentro de um projeto, como em momentos em que você usa um modelo como
vector<int>
várias vezes, você deve usarextern
em todos, exceto um arquivo de origem.Isso também se aplica a classes e funções como uma e até mesmo a funções-membro de modelo.
fonte
Wikipedia tem a melhor descrição
O aviso:
nonstandard extension used...
O Microsoft VC ++ costumava ter uma versão não padrão desse recurso há alguns anos (em C ++ 03). O compilador avisa sobre isso para evitar problemas de portabilidade com o código que também precisava ser compilado em diferentes compiladores.
Observe o exemplo na página vinculada para ver se funciona quase da mesma maneira. Você pode esperar que a mensagem desapareça com versões futuras do MSVC, exceto, é claro, ao usar outras extensões de compilador não padrão ao mesmo tempo.
fonte
std::vector
(com certeza todos usam ),extern
não terá efeito.extern template
só é necessário se a declaração do modelo estiver completaIsso foi sugerido em outras respostas, mas não acho que ênfase suficiente foi dada a isso.
O que isso significa é que, nos exemplos de OPs, o
extern template
não tem efeito porque as definições do modelo nos cabeçalhos estavam incompletas:void f();
: apenas declaração, sem corpoclass foo
: declara o método,f()
mas não tem definiçãoPortanto, eu recomendaria apenas remover a
extern template
definição nesse caso específico: você só precisa adicioná-los se as classes estiverem completamente definidas.Por exemplo:
TemplHeader.h
TemplCpp.cpp
Main.cpp
compilar e visualizar símbolos com
nm
:resultado:
e então
man nm
vemos queU
significa indefinido, então a definição permaneceu apenasTemplCpp
como desejado.Tudo isso se resume à troca de declarações completas de cabeçalho:
extern template
todos os inclusores, o que os programadores provavelmente esquecerão de fazerOutros exemplos são mostrados em: Instanciação explícita do modelo - quando é usado?
Uma vez que o tempo de compilação é tão crítico em grandes projetos, eu recomendo declarações de modelo incompletas, a menos que partes externas absolutamente precisem reutilizar seu código com suas próprias classes personalizadas complexas.
E, nesse caso, eu tentaria primeiro usar o polimorfismo para evitar o problema de tempo de construção e apenas usar modelos se ganhos de desempenho perceptíveis pudessem ser feitos.
Testado no Ubuntu 18.04.
fonte
O problema conhecido com os modelos é o inchaço do código, que é consequência da geração da definição da classe em cada um dos módulos que invoca a especialização do modelo de classe. Para evitar isso, começando com C ++ 0x, pode-se usar a palavra-chave extern na frente da especialização do modelo de classe
A instanciação explícita da classe de modelo deve acontecer apenas em uma única unidade de tradução, de preferência aquela com definição de modelo (MyClass.cpp)
fonte
Se você já usou extern para funções antes, exatamente a mesma filosofia é seguida para modelos. se não, ir embora para funções simples pode ajudar. Além disso, você pode querer colocar o (s) externo (s) no arquivo de cabeçalho e incluir o cabeçalho quando necessário.
fonte