C ++ 템플릿이 왜 불완전한 유형 (선언)을 우회 할 수있는 이유는 무엇입니까?
-
05-07-2019 - |
문제
다음 간단한 프로그램의 세 가지 반복을 시도했습니다. 이것은 컨테이너 및 정화기 클래스 쌍을 작성하려는 매우 단순화 된 시도이지만 불완전한 유형 (전진 선언)으로 문제가 발생했습니다. 나는 이것이 모든 것을 템플릿 한 후에는 실제로 가능하다는 것을 발견했지만 실제로 템플릿 매개 변수를 사용한 경우에만 가능합니다! (나는 이것을 보면서 이것을 깨달았다 Google Sparsetable 코드.)
두 번째로 작동하지 않는 이유를 설명하는 힌트는 무엇입니까? (첫 번째 것이 작동하지 않는 이유를 알고 있습니다. 컴파일러는 컨테이너의 메모리 레이아웃을 알아야합니다.)
미리 감사드립니다.
// 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
메인 이후 정의가 템플릿하면 오류가 발생합니다.
세 번째 예는 전문화입니다 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;
}
템플릿 버전은 템플릿을 인스턴스화 할 때 두 클래스 모두 완전히 정의되므로 작동합니다.
첫 번째 경우, 클래스가 정의되기 전에 컨테이너 클래스의 멤버 기능에 액세스하려고하므로 작동하지 않습니다.
두 번째 경우, 템플릿은 처음으로 특정 유형과 함께 사용될 때 인스턴스화됩니다. 이 시점에서 컨테이너 클래스는 주로 정의되었으므로 컴파일됩니다.
세 번째 경우에는 원형 기준이 있습니다. 컨테이너는 반복을 사용하고 ITER은 컨테이너를 사용하므로 작동 할 수 없습니다.