Como o compilador sabe usar uma especialização de modelo em vez de sua própria instanciação?

StackOverflow https://stackoverflow.com/questions/2058159

  •  20-09-2019
  •  | 
  •  

Pergunta

Considere os seguintes arquivos:

Foo.h

template <typename T>
struct Foo
{
  int foo();
};

template <typename T>
int Foo<T>::foo()
{
  return 6;
}

Foo.c

#include "Foo.H"

template <>
int Foo<int>::foo()
{
  return 7;
}

main.c

#include <iostream>
#include "Foo.H"

using namespace std;

int main()
{
  Foo<int> f;
  cout << f.foo() << endl;
  return 0;
}

Quando eu compilar e executar, 7 é impresso. O que está acontecendo aqui? Quando os modelos são instanciados? Se o compilador faz isso, como o compilador sabe não instanciar sua própria versão do Foo?

Foi útil?

Solução

A questão é que você violou a regra de uma definição. Em main.c, você incluiu foo.h, mas não foo.c (o que faz sentido, pois é um arquivo de origem). Quando o Main.c é compilado, o compilador não sabe que você especializou o modelo no foo.c, por isso usa a versão genérica (que retorna 6) e compila uma classe Foo. Então, quando compila foo.c, vê uma especialização completa que pode compilar agora mesmo - Não precisa esperar que seja instanciado em algum lugar, porque todos os tipos são preenchidos (se você tivesse dois parâmetros de modelo e apenas especializado, esse não seria o caso), e compila um novo e distinto Aula foo.

Normalmente, várias definições para a mesma coisa causam um erro de vinculador. Mas as instanciações de modelos são "símbolos fracos", o que significa que várias definições são permitidas. O vinculador assume que todas as definições são realmente iguais e depois escolhe um aleatoriamente (bem, provavelmente consistentemente o primeiro ou o último, mas apenas como coincidência da implementação).

Por que torná -los símbolos fracos? Como o FOO pode ser usado em vários arquivos de origem, cada um deles é compilado individualmente, e cada vez que Foo é usado em uma unidade de compilação, uma nova instanciação é gerada. Normalmente, eles são redundantes, por isso faz sentido jogá -los fora. Mas você violou essa suposição, fornecendo uma especialização em uma unidade de compilação (Foo.c), mas não a outra (main.c).

Se você declarar a especialização do modelo em foo.h, quando o main.c é compilado, ele não gera uma instanciação do FOO, certificando -se de que exista apenas uma definição em seu programa.

Outras dicas

Eu acho que o compilador instancia foo, mas depois vincular -o escolhe seu foo especializado.

Ao compilar Main.c, o compilador não conhece sua especialização. Eu acho que deve gerar sua própria versão de Foo<int>::foo() com base no modelo não especializado.

Mas então, ao vincular, o ligante vê que uma especialização para Foo<int>::foo() existe. Portanto, ele coloca no executável a versão especializada.

No final, mesmo que não o saiba em tempo de compilação, o main.c chamará a versão especializada de Foo<int>::foo().

Os modelos geram classe diferente para cada combinação de parâmetros de modelo. Isso acontece no tempo de compilação e essa é a razão pela qual os modelos devem residir nos cabeçalhos. Você faz uma especialização para o parâmetro int e as chamadas do compilador Foo<int>::foo() para sua variável f. É como substituir a função virtual, mas no momento da compilação.

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