Почему шаблоны C ++ позволяют мне обходить неполные типы (предварительные объявления)?

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

Вопрос

Я попробовал три итерации следующей простой программы. Это очень упрощенная попытка написать пару классов контейнер и итератор, но я столкнулся с проблемами с неполными типами (предварительные объявления). Я обнаружил, что это действительно возможно, как только я все шаблонизирую - но только если я действительно использовал параметр шаблона! (Я понял это, посмотрев на Google разреженный код .)

Любые подсказки, объясняющие, почему второй работает, а третий нет? (Я знаю, почему первый не работает - компилятор должен знать структуру памяти контейнера.)

Заранее спасибо.

// 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
Это было полезно?

Решение

Первый требует определения container , так как вы выполняете операцию копирования. Если вы определите конструктор iter после определения 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;
}

Второй пример работает, потому что нет класса, пока вы не создадите его в своей функции main . К этому времени все типы определены. Попробуйте переместить любое определение шаблонов iter или container после main, и вы получите ошибку.

Третий пример - это специализация для int или около того. Это должно скомпилироваться, потому что параметр шаблона для iter не используется. Синтаксис специализации немного отключен. Однако нет подходящего конструктора, поэтому вы получите мусор только для x . Более того, итераторы хорошо моделируются указателями. Передача значения this не сильно поможет. Итераторы обычно требуются для последовательности, а не для отдельного объекта. Однако ничто не может помешать вам построить его.

И вам не нужен ; после тела функции.

Другие советы

Вы можете сделать это без шаблонов, определив iter :: iter () после определения контейнера:

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;
}

Версия шаблона работает, потому что при создании экземпляров шаблонов оба класса полностью определены.

В первом случае вы пытаетесь получить доступ к функции-члену класса Container до того, как класс был определен, поэтому это не сработает.

Во втором случае шаблон создается при первом использовании с определенным типом. На этом этапе класс Container был определен, в основном, и поэтому он компилируется.

В третьем случае есть круговая ссылка. контейнер использует iter, а iter использует контейнер, поэтому он не может работать.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top