.hpp
와 .cpp
파일 사이에 분할 된 C ++ 템플릿 클래스를 컴파일하려고하면 오류가 발생 합니다.
$ g++ -c -o main.o main.cpp
$ g++ -c -o stack.o stack.cpp
$ g++ -o main main.o stack.o
main.o: In function `main':
main.cpp:(.text+0xe): undefined reference to 'stack<int>::stack()'
main.cpp:(.text+0x1c): undefined reference to 'stack<int>::~stack()'
collect2: ld returned 1 exit status
make: *** [program] Error 1
내 코드는 다음과 같습니다.
stack.hpp :
#ifndef _STACK_HPP
#define _STACK_HPP
template <typename Type>
class stack {
public:
stack();
~stack();
};
#endif
stack.cpp :
#include <iostream>
#include "stack.hpp"
template <typename Type> stack<Type>::stack() {
std::cerr << "Hello, stack " << this << "!" << std::endl;
}
template <typename Type> stack<Type>::~stack() {
std::cerr << "Goodbye, stack " << this << "." << std::endl;
}
main.cpp :
#include "stack.hpp"
int main() {
stack<int> s;
return 0;
}
ld
물론 정확합니다. 기호가 stack.o
.
에 대한 대답 이 질문은 이 말한대로 내가 이미하고 있어요로서, 도움이되지 않습니다.
이 방법 이 도움 이 될 수 있지만 모든 단일 방법을 .hpp
파일 로 옮기고 싶지는 않습니다. 그럴 필요 는 없습니까?
.cpp
파일의 모든 항목을 파일 로 이동 .hpp
하고 독립 실행 형 개체 파일로 링크하지 않고 모든 항목을 포함 하는 유일한 솔루션 입니까? 즉 보인다 지독하게 못생긴! 이 경우 이전 상태로 되돌리고 이름 stack.cpp
을 바꾸고 stack.hpp
끝낼 수 있습니다.
답변
별도의 cpp 파일에 템플릿 클래스 구현을 작성하고 컴파일하는 것은 불가능합니다. 그렇게하는 모든 방법은 누군가가 주장하는 경우 별도의 cpp 파일 사용을 모방하는 해결 방법이지만 실제로는 템플릿 클래스 라이브러리를 작성하고 구현을 숨기기 위해 헤더 및 lib 파일과 함께 배포하려는 경우에는 불가능합니다. .
이유를 알기 위해 컴파일 과정을 살펴 보겠습니다. 헤더 파일은 컴파일되지 않습니다. 전처리 된 것입니다. 그런 다음 전처리 된 코드는 실제로 컴파일 된 cpp 파일과 결합됩니다. 이제 컴파일러가 객체에 대한 적절한 메모리 레이아웃을 생성해야하는 경우 템플릿 클래스의 데이터 유형을 알아야합니다.
사실 템플릿 클래스는 클래스가 아니라 인자로부터 데이터 타입의 정보를 얻은 후 컴파일 타임에 컴파일러에 의해 선언과 정의가 생성되는 클래스의 템플릿이라는 것을 이해해야합니다. 메모리 레이아웃을 만들 수없는 한 메서드 정의에 대한 지침을 생성 할 수 없습니다. 클래스 메서드의 첫 번째 인수는 ‘this’연산자입니다. 모든 클래스 메서드는 이름이 맹 글링되고 첫 번째 매개 변수가 작동하는 개체로 개별 메서드로 변환됩니다. ‘this’인수는 사용자가 유효한 형식 인수로 개체를 인스턴스화하지 않는 한 컴파일러에서 사용할 수없는 템플릿 클래스의 경우 개체의 크기를 실제로 알려줍니다. 이 경우 메서드 정의를 별도의 cpp 파일에 넣고 컴파일을 시도하면 개체 파일 자체가 클래스 정보로 생성되지 않습니다. 컴파일은 실패하지 않고 개체 파일을 생성하지만 개체 파일의 템플릿 클래스에 대한 코드는 생성하지 않습니다. 이것이 링커가 개체 파일에서 기호를 찾을 수없고 빌드가 실패하는 이유입니다.
이제 중요한 구현 세부 정보를 숨기는 대안은 무엇입니까? 우리 모두가 알고 있듯이 인터페이스를 구현에서 분리하는 주요 목적은 구현 세부 사항을 바이너리 형식으로 숨기는 것입니다. 여기에서 데이터 구조와 알고리즘을 분리해야합니다. 템플릿 클래스는 알고리즘이 아닌 데이터 구조 만 나타내야합니다. 이를 통해 템플릿 화되지 않은 별도의 클래스 라이브러리 (템플릿 클래스에서 작동하는 클래스)에서 더 중요한 구현 세부 정보를 숨기거나 데이터를 보관하는 데 사용할 수 있습니다. 템플릿 클래스에는 실제로 데이터를 할당, 가져 오기 및 설정하는 데 필요한 코드가 더 적습니다. 나머지 작업은 알고리즘 클래스에 의해 수행됩니다.
이 토론이 도움이되기를 바랍니다.
답변
그것은 이다 당신이 필요로 가고있는 것을 인스턴스화 알고있는만큼, 수.
stack.cpp 끝에 다음 코드를 추가하면 작동합니다.
template class stack<int>;
템플릿이 아닌 모든 스택 메서드가 인스턴스화되고 연결 단계가 제대로 작동합니다.
답변
답변
아니요, 불가능합니다. export
모든 의도와 목적을 위해 실제로 존재하지 않는 키워드 없이는 아닙니다 .
최선의 방법은 “.tcc”또는 “.tpp”파일에 함수 구현을 넣고 .hpp 파일 끝에 .tcc 파일을 #include하는 것입니다. 그러나 이것은 단지 화장품 일뿐입니다. 헤더 파일에서 모든 것을 구현하는 것과 동일합니다. 이것은 단순히 템플릿 사용에 대해 지불하는 가격입니다.
답변
템플릿 코드를 헤더와 cpp로 분리하려는 두 가지 주요 이유가 있다고 생각합니다.
하나는 단순한 우아함을위한 것입니다. 우리 모두는 읽고, 관리하고 나중에 재사용 할 수있는 코드를 작성하는 것을 좋아합니다.
기타는 컴파일 시간 단축입니다.
저는 현재 (항상 그렇듯이) OpenCL과 함께 시뮬레이션 소프트웨어를 코딩하고 있으며, HW 기능에 따라 필요에 따라 float (cl_float) 또는 double (cl_double) 유형을 사용하여 실행할 수 있도록 코드를 유지하고 싶습니다. 지금은 코드 시작 부분에 #define REAL을 사용하여 수행되지만 매우 우아하지는 않습니다. 원하는 정밀도를 변경하려면 응용 프로그램을 다시 컴파일해야합니다. 실제 런타임 유형이 없기 때문에 당분간 이것으로 살아야합니다. 운 좋게도 OpenCL 커널은 컴파일 된 런타임이며, 간단한 sizeof (REAL)를 사용하면 그에 따라 커널 코드 런타임을 변경할 수 있습니다.
훨씬 더 큰 문제는 응용 프로그램이 모듈 식이지만 보조 클래스 (예 : 시뮬레이션 상수를 미리 계산하는 클래스)를 개발할 때 템플릿 화해야한다는 것입니다. 이러한 클래스는 모두 클래스 종속성 트리의 맨 위에 한 번 이상 나타납니다. 최종 템플릿 클래스 Simulation은 이러한 팩토리 클래스 중 하나의 인스턴스를 가지므로 사실상 팩토리 클래스를 약간 변경할 때마다 전체 소프트웨어를 재 구축해야합니다. 이것은 매우 성가신 일이지만 더 나은 해결책을 찾을 수없는 것 같습니다.
답변
#include "stack.cpp
끝에있는 경우에만 stack.hpp
. 구현이 비교적 크고 .cpp 파일의 이름을 다른 확장명으로 바꾸는 경우에만이 방법을 권장합니다.
답변
모든 템플릿 매개 변수를 비 템플릿 클래스로 추출 할 수있는 경우 (유형이 안전하지 않을 수 있음) 공통 기능 foo를 추출 할 수 있다면 대부분의 구현을 cpp 파일에 숨길 수 있습니다. 그런 다음 헤더에는 해당 클래스에 대한 리디렉션 호출이 포함됩니다. 유사한 접근 방식이 “템플릿 팽창”문제와 싸울 때 사용됩니다.