헤더 파일에서만 템플릿을 구현할 수있는 이유는 무엇입니까?
C ++ 표준 라이브러리 에서 인용 : 튜토리얼 및 핸드북 :
현재 템플릿을 사용할 수있는 유일한 방법은 인라인 함수를 사용하여 헤더 파일에 구현하는 것입니다.
왜 이런거야?
(설명 : 헤더 파일은 유일한 휴대용 솔루션 은 아니지만 가장 편리한 휴대용 솔루션입니다.)
답변
주의 사항 : 헤더 파일에 구현을 넣을 필요 는 없습니다 .이 답변 끝에있는 대체 솔루션을 참조하십시오.
어쨌든 코드가 실패하는 이유는 템플릿을 인스턴스화 할 때 컴파일러가 주어진 템플릿 인수로 새 클래스를 생성하기 때문입니다. 예를 들면 다음과 같습니다.
template<typename T>
struct Foo
{
T bar;
void doSomething(T param) {/* do stuff using T */}
};
// somewhere in a .cpp
Foo<int> f;
이 줄을 읽을 때 컴파일러는 FooInt
다음과 같은 새 클래스를 만듭니다 (라고 부르겠습니다 ).
struct FooInt
{
int bar;
void doSomething(int param) {/* do stuff using int */}
}
따라서 컴파일러는 템플릿 인수 (이 경우 int
) 로 인스턴스화하기 위해 메서드 구현에 액세스 할 수 있어야합니다 . 이러한 구현이 헤더에 없으면 액세스 할 수 없으므로 컴파일러가 템플릿을 인스턴스화 할 수 없습니다.
이에 대한 일반적인 해결책은 헤더 파일에 템플릿 선언을 작성한 다음 구현 파일 (예 : .tpp)에서 클래스를 구현하고 헤더 끝에이 구현 파일을 포함하는 것입니다.
Foo.h
template <typename T>
struct Foo
{
void doSomething(T param);
};
#include "Foo.tpp"
Foo.tpp
template <typename T>
void Foo<T>::doSomething(T param)
{
//implementation
}
이런 식으로 구현은 여전히 선언과 분리되지만 컴파일러에서 액세스 할 수 있습니다.
대체 솔루션
또 다른 해결책은 구현을 분리하여 유지하고 필요한 모든 템플릿 인스턴스를 명시 적으로 인스턴스화하는 것입니다.
Foo.h
// no implementation
template <typename T> struct Foo { ... };
Foo.cpp
// implementation of Foo's methods
// explicit instantiations
template class Foo<int>;
template class Foo<float>;
// You will only be able to use Foo with int or float
내 설명이 충분히 명확하지 않은 경우이 주제에 대한 C ++ Super-FAQ를 볼 수 있습니다 .
이는 별도의 컴파일이 필요하고 템플릿이 인스턴스화 스타일의 다형성이기 때문입니다.
구체적인 설명을 좀 더 자세히 살펴 보겠습니다. 다음 파일이 있다고 가정합니다.
- foo.h
- 인터페이스를 선언합니다.
class MyClass<T>
- 인터페이스를 선언합니다.
- foo.cpp
- 구현을 정의
class MyClass<T>
- 구현을 정의
- bar.cpp
- 용도
MyClass<int>
- 용도
별도의 컴파일은 foo.cpp 를 bar.cpp와 독립적 으로 컴파일 할 수 있어야 함을 의미합니다 . 컴파일러는 각 컴파일 단위에서 분석, 최적화 및 코드 생성의 모든 노력을 완전히 독립적으로 수행합니다. 전체 프로그램 분석을 할 필요가 없습니다. 전체 프로그램을 한 번에 처리해야하는 것은 링커 뿐이며 링커의 작업은 훨씬 더 쉽습니다.
bar.cpp 는 foo.cpp를 컴파일 할 때 존재하지 않아도 되지만 foo 를 다시 컴파일 할 필요없이 이미 생성 한 foo.o 와 bar.o 를 연결할 수 있어야합니다. .cpp . foo.cpp 는 동적 라이브러리로 컴파일 될 수도 있고, foo.cpp 없이 다른 곳에 배포 될 수도 있고 , 내가 foo.cpp를 작성한 후 몇 년 후에 작성한 코드와 연결될 수도 있습니다.
"Instantiation-style polymorphism"은 템플릿 MyClass<T>
이 모든 값에 대해 작동 할 수있는 코드로 컴파일 될 수있는 제네릭 클래스가 아니라는 것을 의미합니다 T
. 즉, 등 할당 자 및 생성자, C ++ 템플릿 의도 쓰는 것을 피하기 위해되는 함수 포인터를 전달하기 위해 필요한, 권투로 오버 같은 추가 것이 거의 동일한 class MyClass_int
, class MyClass_float
등, 그러나 여전히 컴파일 된 코드로 끝날 수있을 대부분 것처럼 우리는 했다 별도로 각 버전을 썼다. 따라서 템플릿은 말 그대로 템플릿입니다. 클래스 템플릿은 클래스가 아니라T
우리가 만날 때 마다 새 클래스를 만드는 방법입니다 . 템플릿은 코드로 컴파일 할 수 없으며 템플릿을 인스턴스화 한 결과 만 컴파일 할 수 있습니다.
따라서 foo.cpp 가 컴파일 될 때 컴파일러는 bar.cppMyClass<int>
가 필요하다는 것을 알 수 없습니다 . 템플릿을 볼 수 MyClass<T>
있지만 이에 대한 코드를 내보낼 수는 없습니다 (클래스가 아닌 템플릿). 그리고 bar.cpp 가 컴파일되면 컴파일러 는를 생성해야한다는 것을 MyClass<int>
알 수 있지만 템플릿을 볼 수 없으므로 MyClass<T>
( foo.h 의 인터페이스 만 ) 생성 할 수 없습니다.
경우 foo.cpp에 자신이 사용하는 MyClass<int>
컴파일하는 동안, 그것을 위해 다음 코드가 생성됩니다 foo.cpp의를 그렇게 할 때, bar.o가 연결되어 foo.o 가 매여 할 수 있으며 작동합니다. 이 사실을 사용하여 단일 템플릿을 작성하여 유한 한 템플릿 인스턴스화 집합을 .cpp 파일에 구현할 수 있습니다. 그러나 bar.cpp 가 템플릿을 템플릿 으로 사용하고 원하는 유형으로 인스턴스화하는 방법은 없습니다 . foo.cpp 의 작성자 가 제공한다고 생각한 템플릿 클래스의 기존 버전 만 사용할 수 있습니다 .
템플릿을 컴파일 할 때 컴파일러가 "모든 버전을 생성"해야한다고 생각할 수 있습니다. 링크 중에 사용되지 않는 버전은 필터링됩니다. 포인터 및 배열과 같은 "유형 수정 자"기능이 내장 유형조차도 무한한 유형의 유형을 생성 할 수 있도록 허용하기 때문에 이러한 접근 방식이 직면하게되는 엄청난 오버 헤드와 극심한 어려움을 제외하고, 이제 프로그램을 확장하면 어떻게됩니까? 추가하여:
- baz.cpp
- 선언 및 구현
class BazPrivate
및 사용MyClass<BazPrivate>
- 선언 및 구현
우리가하지 않는 한 이것이 작동 할 수있는 방법은 없습니다.
- 다시 컴파일해야 foo.cpp에 우리가 변경할 때마다 프로그램에서 다른 파일을 경우, 그것은 새로운 새로운 인스턴스를 추가
MyClass<T>
- 그 필요 baz.cpp이 (헤더 포함 가능성 통해) 포함의 전체 템플릿을
MyClass<T>
컴파일러가 생성 할 수있는 그래서,MyClass<BazPrivate>
컴파일시 baz.cpp .
전체 프로그램 분석 컴파일 시스템이 컴파일하는 데 시간이 오래 걸리고 소스 코드없이 컴파일 된 라이브러리를 배포 할 수 없기 때문에 아무도 좋아하지 않습니다 (1) . 따라서 대신 (2)가 있습니다.
여기에 많은 정답이 있지만 (완전성을 위해) 이것을 추가하고 싶었습니다.
구현 cpp 파일의 맨 아래에서 템플릿과 함께 사용할 모든 유형의 명시 적 인스턴스화를 수행하면 링커가 평상시처럼 해당 유형을 찾을 수 있습니다.
편집 : 명시 적 템플릿 인스턴스화의 예 추가. 템플릿이 정의되고 모든 멤버 함수가 정의 된 후에 사용됩니다.
template class vector<int>;
이렇게하면 클래스와 모든 멤버 함수 (전용)를 인스턴스화하여 링커에서 사용할 수 있습니다. 유사한 구문이 템플릿 함수에 대해 작동하므로 멤버가 아닌 연산자 오버로드가있는 경우에도 동일한 작업을 수행해야 할 수 있습니다.
위의 예는 벡터를 사용 extern template class vector<int>
하는 다른 모든 (1000?) 파일 에서 인스턴스화하지 못하도록 일반 포함 파일 (미리 컴파일 된 헤더?)이 사용하는 경우를 제외하고는 벡터가 헤더에 완전히 정의되어 있기 때문에 상당히 쓸모가 없습니다 .
템플릿은 실제로 개체 코드로 컴파일하기 전에 컴파일러에 의해 인스턴스화 되어야합니다. 이 인스턴스화는 템플릿 인수가 알려진 경우에만 수행 할 수 있습니다. 이제 템플릿 함수가에서 선언되고 a.h
에서 정의 a.cpp
되고 사용되는 시나리오를 상상해보십시오 b.cpp
. a.cpp
를 컴파일 할 때 다가오는 컴파일 b.cpp
에 템플릿 인스턴스가 필요한지 여부는 물론 특정 인스턴스가 필요 하다는 것이 반드시 알려지지는 않습니다 . 더 많은 헤더 및 소스 파일의 경우 상황이 빠르게 더 복잡해질 수 있습니다.
컴파일러는 템플릿의 모든 사용에 대해 "미리보기"를 위해 더 현명하게 만들 수 있다고 주장 할 수 있지만 재귀 적이거나 복잡한 시나리오를 만드는 것이 어렵지 않을 것이라고 확신합니다. AFAIK, 컴파일러는 그런 예측을하지 않습니다. Anton이 지적했듯이 일부 컴파일러는 템플릿 인스턴스화의 명시 적 내보내기 선언을 지원하지만 모든 컴파일러가이를 지원하지는 않습니다 (아직?).
사실, C ++ (11) 이전에 표준이 정의 된 export
키워드 것이 가능 헤더 파일에 템플릿을 선언하고 다른 곳을 구현합니다.
인기있는 컴파일러 중 어느 것도이 키워드를 구현하지 않았습니다. 내가 아는 유일한 것은 Comeau C ++ 컴파일러에서 사용하는 Edison Design Group에서 작성한 프론트 엔드입니다. 컴파일러는 적절한 인스턴스화를 위해 템플릿 정의가 필요하기 때문에 다른 모든 것들은 헤더 파일에 템플릿을 작성해야했습니다 (다른 사람들이 이미 지적했듯이).
결과적으로 ISO C ++ 표준위원회는 export
C ++ 11에서 템플릿 의 기능 을 제거하기로 결정했습니다 .
표준 C ++에는 이러한 요구 사항이 없지만 일부 컴파일러에서는 사용되는 모든 번역 단위에서 모든 함수 및 클래스 템플릿을 사용할 수 있어야합니다. 실제로 이러한 컴파일러의 경우 템플릿 함수의 본문을 헤더 파일에서 사용할 수 있어야합니다. 반복하려면 해당 컴파일러가 .cpp 파일과 같은 헤더가 아닌 파일에서 정의되는 것을 허용하지 않습니다.
이 문제를 완화 할 수 있는 내보내기 키워드가 있지만 이식성에 가까운 곳은 없습니다.
템플릿 매개 변수에 대해 주어진 / 추론 된 매개 변수에 따라 컴파일러가 다른 버전의 코드를 인스턴스화해야하므로 헤더에서 템플릿을 사용해야합니다. 템플릿은 코드를 직접 나타내는 것이 아니라 해당 코드의 여러 버전에 대한 템플릿입니다. .cpp
파일 에서 템플릿이 아닌 함수를 컴파일하면 구체적인 함수 / 클래스를 컴파일하는 것입니다. 이것은 다른 유형으로 인스턴스화 될 수있는 템플릿의 경우가 아닙니다. 즉, 템플릿 매개 변수를 구체적인 유형으로 대체 할 때 구체적인 코드를 내 보내야합니다.
export
별도의 컴파일에 사용되는 키워드 가있는 기능이 있습니다. 이 export
기능은 더 이상 사용되지 않으며 C++11
AFAIK에서는 하나의 컴파일러 만 구현했습니다. 당신은 사용하지합니다 export
. 개별 컴파일은 C++
또는 에서 가능하지 C++11
않지만에서 C++17
개념이 만들어지면 별도의 컴파일 방법을 가질 수 있습니다.
별도의 컴파일을 수행하려면 별도의 템플릿 본문 검사가 가능해야합니다. 개념으로 해결이 가능한 것 같습니다. 최근 표준위원회 회의에서 발표 된 이 문서를 살펴보십시오 . 사용자 코드에서 템플릿 코드에 대한 코드를 인스턴스화해야하기 때문에 이것이 유일한 요구 사항은 아니라고 생각합니다.
템플릿에 대한 별도의 컴파일 문제는 현재 작업중인 모듈로의 마이그레이션에서 발생하는 문제이기도합니다.
편집 : 2020 년 8 월 현재 모듈은 이미 C ++의 현실입니다. https://en.cppreference.com/w/cpp/language/modules
위에 많은 좋은 설명이 있지만 템플릿을 헤더와 본문으로 분리하는 실용적인 방법이 없습니다.
내 주요 관심사는 정의를 변경할 때 모든 템플릿 사용자의 재 컴파일을 피하는 것입니다.
템플릿 본문에 모든 템플릿 인스턴스화를 갖는 것은 저에게 실행 가능한 솔루션이 아닙니다. 템플릿 작성자는 그 사용법을 모두 알 수없고 템플릿 사용자가 수정할 권한이 없을 수 있기 때문입니다.
이전 컴파일러 (gcc 4.3.4, aCC A.03.13)에서도 작동하는 다음 접근 방식을 사용했습니다.
각 템플릿 사용에 대해 자체 헤더 파일 (UML 모델에서 생성됨)에 typedef가 있습니다. 본문에는 인스턴스화가 포함되어 있습니다 (끝에 연결된 라이브러리에서 끝남).
템플릿의 각 사용자는 해당 헤더 파일을 포함하고 typedef를 사용합니다.
회로도 예 :
MyTemplate.h :
#ifndef MyTemplate_h
#define MyTemplate_h 1
template <class T>
class MyTemplate
{
public:
MyTemplate(const T& rt);
void dump();
T t;
};
#endif
MyTemplate.cpp :
#include "MyTemplate.h"
#include <iostream>
template <class T>
MyTemplate<T>::MyTemplate(const T& rt)
: t(rt)
{
}
template <class T>
void MyTemplate<T>::dump()
{
cerr << t << endl;
}
MyInstantiatedTemplate.h :
#ifndef MyInstantiatedTemplate_h
#define MyInstantiatedTemplate_h 1
#include "MyTemplate.h"
typedef MyTemplate< int > MyInstantiatedTemplate;
#endif
MyInstantiatedTemplate.cpp :
#include "MyTemplate.cpp"
template class MyTemplate< int >;
main.cpp :
#include "MyInstantiatedTemplate.h"
int main()
{
MyInstantiatedTemplate m(100);
m.dump();
return 0;
}
이렇게하면 모든 템플릿 사용자 (및 종속성)가 아닌 템플릿 인스턴스화 만 다시 컴파일하면됩니다.
즉, 템플릿 클래스의 메서드 구현을 정의하는 가장 이식 가능한 방법은 템플릿 클래스 정의 내에서 정의하는 것입니다.
template < typename ... >
class MyClass
{
int myMethod()
{
// Not just declaration. Add method implementation here
}
};
여기에 주목할만한 것을 추가하십시오. 함수 템플릿이 아닌 경우 구현 파일에서 템플릿 클래스의 메서드를 잘 정의 할 수 있습니다.
myQueue.hpp :
template <class T>
class QueueA {
int size;
...
public:
template <class T> T dequeue() {
// implementation here
}
bool isEmpty();
...
}
myQueue.cpp :
// implementation of regular methods goes like this:
template <class T> bool QueueA<T>::isEmpty() {
return this->size == 0;
}
main()
{
QueueA<char> Q;
...
}
.h를 사용하는 모든 .cpp 모듈의 일부로 .h를 컴파일하여 생성되는 추가 컴파일 시간과 바이너리 크기 부풀림이 우려되는 경우, 많은 경우 템플릿 클래스를 템플릿 화되지 않은 기본 클래스에서 하위 클래스로 만드는 것입니다. 인터페이스의 유형에 종속되지 않는 부분 및 해당 기본 클래스는 .cpp 파일에서 구현할 수 있습니다.
컴파일러가 할당을위한 유형을 알아야하기 때문에 정확합니다. 따라서 헤더 파일은 c / cpp 파일과 달리 컴파일되지 않기 때문에 템플릿 클래스, 함수, 열거 형 등을 공개하거나 라이브러리의 일부 (정적 또는 동적)로 만들려면 헤더 파일에서도 구현해야합니다. 아르. 컴파일러가 유형을 모르면 컴파일 할 수 없습니다. .Net에서는 모든 객체가 Object 클래스에서 파생되기 때문에 가능합니다. 이것은 .Net이 아닙니다.
컴파일러는 컴파일 단계에서 템플릿을 사용할 때 각 템플릿 인스턴스화에 대한 코드를 생성합니다. 컴파일 및 링크 프로세스에서 .cpp 파일은 main.cpp에 포함 된 .h 파일이 아직 구현되지 않았기 때문에 참조 또는 정의되지 않은 기호를 포함하는 순수 객체 또는 기계 코드로 변환됩니다. 이들은 템플릿에 대한 구현을 정의하는 다른 개체 파일과 연결할 준비가되어 있으므로 전체 a.out 실행 파일을 갖게됩니다.
그러나 정의한 각 템플릿 인스턴스화에 대한 코드를 생성하려면 템플릿을 컴파일 단계에서 처리해야하므로 헤더 파일과 별도로 템플릿을 컴파일하는 것만으로는 작동하지 않습니다. 그 이유는 바로 그 이유 때문입니다. 각 템플릿 인스턴스화는 문자 그대로 완전히 새로운 클래스입니다. 일반 클래스에서는 .h와 .cpp를 분리 할 수 있습니다. .h는 해당 클래스의 청사진이고 .cpp는 원시 구현이므로 모든 구현 파일을 정기적으로 컴파일하고 링크 할 수 있지만 템플릿을 사용하는 것은 .h를 사용하는 방법에 대한 청사진입니다. 클래스는 템플릿 .cpp 파일이 클래스의 원시 일반 구현이 아니라 단순히 클래스의 청사진이므로 객체가 어떻게 보이는지가 아니라 .h 템플릿 파일의 모든 구현을 컴파일 할 수 없습니다. 컴파일하려면 구체적인 무언가가 필요합니다. 템플릿은 그런 의미에서 추상적입니다.
따라서 템플릿은 개별적으로 컴파일되지 않으며 다른 소스 파일에 구체적인 인스턴스화가있는 경우에만 컴파일됩니다. 그러나 구체적인 인스턴스화는 템플릿 파일의 구현을 알아야합니다 typename T
. .h 파일에서 구체적인 유형을 사용하여 수정하는 것은 .cpp가 링크 할 수 있기 때문에 작업을 수행하지 않기 때문입니다. 나중에는 템플릿이 추상적이고 컴파일 할 수 없기 때문에 지금 구현을 제공해야하므로 컴파일 및 링크 할 내용을 알 수 있으며, 이제 구현이 완료되었으므로 포함 된 소스 파일에 링크됩니다. 기본적으로 템플릿을 인스턴스화하는 순간 완전히 새로운 클래스를 만들어야합니다. 컴파일러에 알리지 않는 한 내가 제공하는 유형을 사용할 때 해당 클래스가 어떻게 생겼는지 모르겠다면 그렇게 할 수 없습니다. 이제 컴파일러는 T
내 유형으로 대체 하고 컴파일 및 링크 할 준비가 된 구체적인 클래스를 만들 수 있습니다.
요약하면 템플릿은 클래스가 어떻게 보이는지에 대한 청사진이고 클래스는 객체가 어떻게 보이는지에 대한 청사진입니다. 컴파일러는 구체적인 유형 만 컴파일합니다. 즉, 적어도 C ++의 템플릿은 순수한 언어 추상화이기 때문에 구체적인 인스턴스화와 별도로 템플릿을 컴파일 할 수 없습니다. 말하자면 템플릿의 추출을 해제해야하며, 템플릿 추상화가 일반 클래스 파일로 변환되고 정상적으로 컴파일 될 수 있도록 처리 할 구체적인 유형을 제공하여이를 수행합니다. 템플릿 .h 파일과 템플릿 .cpp 파일을 분리하는 것은 의미가 없습니다. .cpp와 .h의 분리는 .cpp가 개별적으로 컴파일되고 템플릿과 함께 개별적으로 링크 될 수있는 곳일 뿐이 기 때문에 의미가 없습니다. 구체적인 인스턴스화는 항상 사용되는 유형에 대해 알아야하는 구체적인 인스턴스화와 함께 추상화를 항상 함께 두십시오.
의미 typename T
GET은 컴파일 단계하지 그래서하지 않고 템플릿을 컴파일하려고하면 연결 단계에서 대체있어 T
컴파일러와 결과 오브젝트 코드로 완전히 의미가 그렇지 않기 때문에 만들 수 없습니다 구체적인 값 유형으로 대체되고 뭔지 알아 T
.
기술적으로 template.cpp 파일을 저장하고 다른 소스에서 찾을 때 유형을 전환하는 일종의 기능을 만드는 것이 가능합니다. 표준에는 export
템플릿을 별도의 파일에 넣을 수 있는 키워드가 있다고 생각합니다. cpp 파일이지만 많은 컴파일러가 실제로 이것을 구현하지는 않습니다.
참고로 템플릿 클래스에 대한 전문화를 수행 할 때 구현에서 헤더를 분리 할 수 있습니다. 정의에 따른 전문화는 개별적으로 컴파일 및 링크 할 수있는 구체적인 유형을 전문화한다는 것을 의미하기 때문입니다.
별도의 구현 방법은 다음과 같습니다.
//inner_foo.h
template <typename T>
struct Foo
{
void doSomething(T param);
};
//foo.tpp
#include "inner_foo.h"
template <typename T>
void Foo<T>::doSomething(T param)
{
//implementation
}
//foo.h
#include <foo.tpp>
//main.cpp
#include <foo.h>
inner_foo에는 전방 선언이 있습니다. foo.tpp에는 구현이 있으며 inner_foo.h를 포함합니다. 그리고 foo.h는 foo.tpp를 포함하기 위해 한 줄만 가질 것입니다.
컴파일 시간에 foo.h의 내용이 foo.tpp에 복사 된 다음 전체 파일이 foo.h에 복사 된 후 컴파일됩니다. 이렇게하면 제한이 없으며 하나의 추가 파일에 대한 대가로 이름 지정이 일관됩니다.
* .tpp에서 클래스의 포워드 선언을 볼 수 없을 때 코드에 대한 정적 분석기가 중단되기 때문에이 작업을 수행합니다. IDE에서 코드를 작성하거나 YouCompleteMe 또는 기타를 사용할 때 이것은 성가신 일입니다.
템플릿 인스턴스화를위한 "cfront"모델과 "borland"모델 사이의 장단점을 설명하는이 gcc 페이지를 살펴볼 것을 제안합니다.
https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Template-Instantiation.html
"borland"모델은 작성자가 제안한 내용과 일치하며 전체 템플릿 정의를 제공하고 여러 번 컴파일되도록합니다.
수동 및 자동 템플릿 인스턴스화 사용과 관련된 명시 적 권장 사항이 포함되어 있습니다. 예를 들어 "-repo"옵션을 사용하여 인스턴스화해야하는 템플릿을 수집 할 수 있습니다. 또는 다른 옵션은 "-fno-implicit-templates"를 사용하여 자동 템플릿 인스턴스화를 비활성화하여 수동 템플릿 인스턴스화를 강제하는 것입니다.
내 경험상 C ++ 표준 라이브러리와 각 컴파일 단위 (템플릿 라이브러리 사용)에 대해 인스턴스화되는 Boost 템플릿에 의존합니다. 큰 템플릿 클래스의 경우 필요한 유형에 대해 수동 템플릿 인스턴스화를 한 번 수행합니다.
다른 프로그램에서 사용할 수있는 템플릿 라이브러리가 아닌 작업 프로그램을 제공하고 있기 때문에 이것이 제 접근 방식입니다. 책의 저자 인 Josuttis는 템플릿 라이브러리에서 많은 작업을합니다.
속도가 정말 걱정된다면 미리 컴파일 된 헤더를 사용하여 탐색 할 것입니다. https://gcc.gnu.org/onlinedocs/gcc/Precompiled-Headers.html
많은 컴파일러에서 지원을 받고 있습니다. 그러나 템플릿 헤더 파일에서는 미리 컴파일 된 헤더가 어려울 것이라고 생각합니다.