Por que modelos C ++, deixe-me contornar tipos incompletos (declarações para a frente)?

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

Pergunta

Eu tentei três iterações do programa simples seguinte. Esta é uma tentativa muito simplificado para escrever um par recipiente-and-iterador de aulas, mas eu estava correndo em problemas com tipos incompletos (declarações para a frente). Eu descobri que isso era de fato possível uma vez que eu templatized tudo - mas apenas se eu realmente usou o parâmetro do modelo! (Eu percebi isso olhando para o Google código sparsetable .)

Todas as dicas explicando por que o segundo funciona enquanto o terceiro não? (Eu sei porque o primeiro não funciona - as necessidades do compilador para saber o layout de memória do recipiente.)

Agradecemos antecipadamente.

// This doesn't work: invalid use of incomplete type.
#if 0
struct container;
struct iter {
  container &c;
  int *p;
  iter(container &c) : c(c), p(&c.value()) {}
};
struct container {
  int x;
  int &value() { return x; }
  iter begin() { return iter(*this); }
};
int main() {
  container c;
  c.begin();
  return 0;
}
#endif

// This *does* work.
template<typename T> struct container;
template<typename T> struct iter {
  container<T> &c;
  T *p;
  iter(container<T> &c) : c(c), p(&c.value()) {}
};
template<typename T> struct container {
  T x;
  T &value() { return x; }
  iter<T> begin() { return iter<T>(*this); }
};
int main() {
  container<int> c;
  c.begin();
  return 0;
};

// This doesn't work either.
#if 0
template<typename T> struct container;
template<typename T> struct iter {
  container<int> &c;
  int *p;
  iter(container<int> &c) : c(c), p(&c.value()) {}
};
template<typename T> struct container {
  int x;
  int &value() { return x; }
  iter<int> begin() { return iter<int>(*this); }
};
int main() {
  container<int> c;
  c.begin();
  return 0;
}
#endif
Foi útil?

Solução

O primeiro exige uma definição de container desde que você está fazendo uma operação de cópia. Se você definir o construtor de iter após a definição de container você estaria bem. Assim:

struct container;
struct iter {
  container &c;
  int *p;
  iter(container &c);
};

struct container {
  int x;
  int &value() { return x; }
  iter begin() { return iter(*this); }
};

iter::iter(container &c) : c(c), p(&c.value()) {}

int main() {
  container c;
  c.begin();
  return 0;
}

O segundo exemplo funciona porque não há nenhuma classe até que um realmente instanciar em sua função main. Por esse tempo todos os tipos são definidos. Tente mover qualquer da definição iter ou modelos container após principal e você vai bater um erro.

O terceiro exemplo é uma especialização para int ou assim parece. Isso deve compilar porque o parâmetro de modelo para iter não é usado. Você tem a especialização sintaxe um pouco fora. No entanto, não há nenhum construtor adequada para que você só vai ter de lixo para x. Além disso, iteradores são bem modelado por ponteiros. Passando valor de this não será de muita ajuda. Iterators são normalmente necessários para uma sequência e não um objecto individual. Embora, não há nada que possa impedi-lo de construir um.

E você não precisa de um ; depois de um corpo da função.

Outras dicas

Você poderia fazer isso sem modelos, definindo iter :: iter () após definição do container:

struct container;

struct iter {
  container &c;
  int *p;
  iter(container &c);
};

struct container {
  int x;
  int &value() { return x; }
  iter begin() { return iter(*this); }
};

iter::iter(container &c)
    : c(c), p(&c.value()) {}

int main() {
  container c;
  c.begin();
  return 0;
}

obras de versão do modelo, porque quando você instanciar modelos de ambas as classes estão completamente definidos.

No primeiro caso, você está tentando acessar uma função membro da classe Container antes da classe foi definida, então isso não vai funcionar.

No segundo caso, o modelo é instanciado pela primeira vez ele é usado com um tipo particular. Nesse ponto, a classe Container foi definido, no principal, e assim ele compila.

No terceiro caso, não há uma referência circular. usos recipiente iter e iter usa recipiente, por isso pode não trabalho.

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