수색…
소개
클래스, 함수 및 (C ++ 14 이후) 변수는 템플리트 될 수 있습니다. 템플릿은 모든 매개 변수가 지정된 경우 구체적인 클래스, 함수 또는 변수가 될 일부 무료 매개 변수가있는 코드 조각입니다. 매개 변수는 유형, 값 또는 자체 템플릿 일 수 있습니다. 잘 알려진 템플릿은 std::vector
, 요소 유형이 지정 될 때 구체적인 컨테이너 유형이됩니다 ( 예 : std::vector<int>
.
통사론
- 템플릿 < template-parameter-list > 선언
- C ++ 11까지 템플릿 < template-parameter-list > 선언 / * 내보내기 * /
- 템플릿 <> 선언
- 템플릿 선언
- extern 템플릿 선언 / * C ++ 11 이후 * /
- template < template-parameter-list > 클래스 ... ( opt ) 식별자 ( opt )
- template < template-parameter-list > 클래스 식별자 ( opt ) = id-expression
- template < template-parameter-list > typename ... ( opt ) 식별자 ( opt ) / * 이후 C ++ 17 * /
- template < template-parameter-list > typename 식별자 ( opt ) = id-expression / * 이후 C ++ 17 * /
- postfix-expression . 템플릿 ID 표현
- postfix-expression -> template id-expression
- 중첩 이름 지정자
template
simple-template-id::
비고
단어 template
는 컨텍스트에 따라 C ++ 언어에서 5 가지 다른 의미를 갖는 키워드 입니다.
<>
묶인 템플릿 매개 변수 목록이 나오면 클래스 템플릿 , 함수 템플릿 또는 기존 템플릿의 부분 전문화 와 같은 템플릿을 선언합니다.template <class T> void increment(T& x) { ++x; }
뒤에 빈
<>
이 있으면 명시 적 (전체) 특성화를 선언합니다.template <class T> void print(T x); template <> // <-- keyword used in this sense here void print(const char* s) { // output the content of the string printf("%s\n", s); }
<>
없는 선언이 나올 경우 명시적인 인스턴스화 선언 또는 정의를 형성합니다.template <class T> std::set<T> make_singleton(T x) { return std::set<T>(x); } template std::set<int> make_singleton(int x); // <-- keyword used in this sense here
템플릿 매개 변수 목록 내에서, 그것은 도입 템플릿 템플릿 매개 변수를 .
template <class T, template <class U> class Alloc> // ^^^^^^^^ keyword used in this sense here class List { struct Node { T value; Node* next; }; Alloc<Node> allocator; Node* allocate_node() { return allocator.allocate(sizeof(T)); } // ... };
범위 분석 연산자
::
및 클래스 멤버 액세스 연산자 뒤에.
및->
이면 다음 이름이 템플리트임을 지정합니다.struct Allocator { template <class T> T* allocate(); }; template <class T, class Alloc> class List { struct Node { T value; Node* next; } Alloc allocator; Node* allocate_node() { // return allocator.allocate<Node>(); // error: < and > are interpreted as // comparison operators return allocator.template allocate<Node>(); // ok; allocate is a template // ^^^^^^^^ keyword used in this sense here } };
C ++ 11 이전에는 템플릿을 export
키워드 로 선언하여 내 보낸 템플릿으로 만들 수있었습니다. 내 보낸 템플리트의 정의가 템플리트가 인스턴스화되는 모든 변환 단위에 존재할 필요는 없습니다. 예를 들어 다음과 같은 작업이 필요했습니다.
foo.h
:
#ifndef FOO_H
#define FOO_H
export template <class T> T identity(T x);
#endif
foo.cpp
:
#include "foo.h"
template <class T> T identity(T x) { return x; }
main.cpp
:
#include "foo.h"
int main() {
const int x = identity(42); // x is 42
}
구현의 어려움으로 인해, export
키워드는 대부분의 주요 컴파일러에서 지원되지 않았습니다. 이것은 C ++ 11에서 삭제되었습니다. 이제 export
키워드를 사용하는 것은 불법입니다. 대신에, (보통 헤더에 정의되어 있지 않은 템플릿 함수 대조적) 헤더 템플릿을 정의하는 것이 필요하다. 헤더 파일에 템플릿 만 구현할 수있는 이유는 무엇입니까?를 참조하십시오 .
함수 템플릿
템플릿은 동일한 효과로 함수 (더 전통적인 구조)에도 적용될 수 있습니다.
// 'T' stands for the unknown type
// Both of our arguments will be of the same type.
template<typename T>
void printSum(T add1, T add2)
{
std::cout << (add1 + add2) << std::endl;
}
그런 다음 구조 템플릿과 동일한 방식으로 사용할 수 있습니다.
printSum<int>(4, 5);
printSum<float>(4.5f, 8.9f);
이 두 경우 모두 템플리트 인수는 매개 변수 유형을 대체하는 데 사용됩니다. 결과는 일반 C ++ 함수처럼 작동합니다 (매개 변수가 컴파일러가 표준 변환을 적용하는 템플릿 유형과 일치하지 않는 경우).
템플리트 함수 (템플리트 클래스와 달리)의 또 다른 특성은 컴파일러가 함수에 전달 된 매개 변수를 기반으로 템플리트 매개 변수를 유추 할 수 있다는 것입니다.
printSum(4, 5); // Both parameters are int.
// This allows the compiler deduce that the type
// T is also int.
printSum(5.0, 4); // In this case the parameters are two different types.
// The compiler is unable to deduce the type of T
// because there are contradictions. As a result
// this is a compile time error.
이 기능을 사용하면 템플릿 구조와 함수를 결합 할 때 코드를 단순화 할 수 있습니다. 표준 라이브러리에는 make_X()
도우미 함수를 사용하여 template structure X
를 만들 수있는 공통 패턴이 있습니다.
// The make_X pattern looks like this.
// 1) A template structure with 1 or more template types.
template<typename T1, typename T2>
struct MyPair
{
T1 first;
T2 second;
};
// 2) A make function that has a parameter type for
// each template parameter in the template structure.
template<typename T1, typename T2>
MyPair<T1, T2> make_MyPair(T1 t1, T2 t2)
{
return MyPair<T1, T2>{t1, t2};
}
어떻게 도움이됩니까?
auto val1 = MyPair<int, float>{5, 8.7}; // Create object explicitly defining the types
auto val2 = make_MyPair(5, 8.7); // Create object using the types of the paramters.
// In this code both val1 and val2 are the same
// type.
참고 : 이것은 코드를 단축하도록 설계되지 않았습니다. 이는 코드를보다 강력하게 만들어줍니다. 여러 위치가 아닌 단일 위치에서 코드를 변경하여 유형을 변경할 수 있습니다.
인수 전달
템플릿은 전달 참조를 사용하여 lvalue 및 rvalue 참조를 모두 허용 할 수 있습니다.
template <typename T>
void f(T &&t);
이 경우 실제 유형 t
는 컨텍스트에 따라 추론됩니다.
struct X { };
X x;
f(x); // calls f<X&>(x)
f(X()); // calls f<X>(x)
첫 번째 경우에 유형 T
는 X
( X&
)에 대한 참조로 추론되고 t
유형은 X
대한 좌변 값 참조 이고 두 번째 경우에는 T
유형이 X
로 추론되고 t
유형 참조 의 유형 ~ X
( X&&
).
주의 : 첫 번째 경우에 decltype(t)
는 T
와 같지만 두 번째 경우에는 그렇지 않다는 점에 주목할 필요가 있습니다.
lvalue 또는 rvalue 레퍼런스인지 여부에 관계없이 t
를 다른 함수로 완벽하게 전달하려면 std::forward
사용해야 std::forward
.
template <typename T>
void f(T &&t) {
g(std::forward<T>(t));
}
전달 참조는 가변 템플릿과 함께 사용할 수 있습니다.
template <typename... Args>
void f(Args&&... args) {
g(std::forward<Args>(args)...);
}
참고 : 전달 참조는 예를 들어 다음 코드에서와 같이 템플릿 매개 변수에만 사용할 수 있습니다. v
는 전달 참조가 아닌 rvalue 참조입니다.
#include <vector>
template <typename T>
void f(std::vector<T> &&v);
기본 클래스 템플릿
클래스 템플릿의 기본 아이디어는 템플릿 매개 변수가 컴파일 타임에 유형으로 대체된다는 것입니다. 결과적으로 동일한 클래스가 여러 유형에 대해 재사용 될 수 있습니다. 사용자는 클래스의 변수가 선언 될 때 사용할 유형을 지정합니다. 세 가지 예제는 main()
:
#include <iostream>
using std::cout;
template <typename T> // A simple class to hold one number of any type
class Number {
public:
void setNum(T n); // Sets the class field to the given number
T plus1() const; // returns class field's "follower"
private:
T num; // Class field
};
template <typename T> // Set the class field to the given number
void Number<T>::setNum(T n) {
num = n;
}
template <typename T> // returns class field's "follower"
T Number<T>::plus1() const {
return num + 1;
}
int main() {
Number<int> anInt; // Test with an integer (int replaces T in the class)
anInt.setNum(1);
cout << "My integer + 1 is " << anInt.plus1() << "\n"; // Prints 2
Number<double> aDouble; // Test with a double
aDouble.setNum(3.1415926535897);
cout << "My double + 1 is " << aDouble.plus1() << "\n"; // Prints 4.14159
Number<float> aFloat; // Test with a float
aFloat.setNum(1.4);
cout << "My float + 1 is " << aFloat.plus1() << "\n"; // Prints 2.4
return 0; // Successful completion
}
템플릿 전문화
템플릿 클래스 / 메소드의 특정 인스턴스화에 대한 구현을 정의 할 수 있습니다.
예를 들면 :
template <typename T>
T sqrt(T t) { /* Some generic implementation */ }
다음과 같이 작성할 수 있습니다.
template<>
int sqrt<int>(int i) { /* Highly optimized integer implementation */ }
그런 다음 sqrt(4.0)
를 쓰는 사용자는 일반 구현을 얻지 만 sqrt(4)
는 특수 구현을 가져옵니다.
부분 템플릿 전문화
전체 템플릿 특수화 부분 템플릿 전문화와 달리 일부 기존 템플릿 고정 인수를 사용하여 템플릿을 도입 할 수 있습니다. 부분 템플릿 전문화는 템플릿 클래스 / 구조체에서만 사용할 수 있습니다.
// Common case:
template<typename T, typename U>
struct S {
T t_val;
U u_val;
};
// Special case when the first template argument is fixed to int
template<typename V>
struct S<int, V> {
double another_value;
int foo(double arg) {// Do something}
};
위에 표시된 것처럼 부분 템플릿 전문화는 완전히 다른 데이터 및 기능 멤버 집합을 도입 할 수 있습니다.
부분적으로 특수화 된 템플릿이 인스턴스화되면 가장 적합한 전문화가 선택됩니다. 예를 들어 템플릿과 두 가지 부분 특수화를 정의 해 보겠습니다.
template<typename T, typename U, typename V>
struct S {
static void foo() {
std::cout << "General case\n";
}
};
template<typename U, typename V>
struct S<int, U, V> {
static void foo() {
std::cout << "T = int\n";
}
};
template<typename V>
struct S<int, double, V> {
static void foo() {
std::cout << "T = int, U = double\n";
}
};
이제 다음 호출 :
S<std::string, int, double>::foo();
S<int, float, std::string>::foo();
S<int, double, std::string>::foo();
인쇄 할 것이다.
General case
T = int
T = int, U = double
함수 템플릿은 완전히 전문화되어있을 수 있습니다.
template<typename T, typename U>
void foo(T t, U u) {
std::cout << "General case: " << t << " " << u << std::endl;
}
// OK.
template<>
void foo<int, int>(int a1, int a2) {
std::cout << "Two ints: " << a1 << " " << a2 << std::endl;
}
void invoke_foo() {
foo(1, 2.1); // Prints "General case: 1 2.1"
foo(1,2); // Prints "Two ints: 1 2"
}
// Compilation error: partial function specialization is not allowed.
template<typename U>
void foo<std::string, U>(std::string t, U u) {
std::cout << "General case: " << t << " " << u << std::endl;
}
기본 템플릿 매개 변수 값
함수 인수처럼 템플릿 매개 변수의 기본값을 가질 수 있습니다. 템플릿 매개 변수 목록의 끝에는 기본값이있는 모든 템플릿 매개 변수를 선언해야합니다. 기본 개념은 템플릿 인스턴스화 중에 기본값을 사용하는 템플릿 매개 변수를 생략 할 수 있다는 것입니다.
기본 템플릿 매개 변수 값 사용의 간단한 예 :
template <class T, size_t N = 10>
struct my_array {
T arr[N];
};
int main() {
/* Default parameter is ignored, N = 5 */
my_array<int, 5> a;
/* Print the length of a.arr: 5 */
std::cout << sizeof(a.arr) / sizeof(int) << std::endl;
/* Last parameter is omitted, N = 10 */
my_array<int> b;
/* Print the length of a.arr: 10 */
std::cout << sizeof(b.arr) / sizeof(int) << std::endl;
}
별칭 템플릿
기본 예 :
template<typename T> using pointer = T*;
이 정의는 pointer<T>
를 T*
의 별칭으로 만듭니다. 예 :
pointer<int> p = new int; // equivalent to: int* p = new int;
별칭 템플릿은 특수화 할 수 없습니다. 그러나 구조체에서 중첩 형식을 참조하도록함으로써 해당 기능을 간접적으로 얻을 수 있습니다.
template<typename T>
struct nonconst_pointer_helper { typedef T* type; };
template<typename T>
struct nonconst_pointer_helper<T const> { typedef T* type; };
template<typename T> using nonconst_pointer = nonconst_pointer_helper<T>::type;
템플릿 템플릿 매개 변수
때로는 값을 수정하지 않고 템플리트 유형을 템플리트에 전달하려고합니다. 이것은 템플릿 템플릿 매개 변수가 생성되는 것입니다. 매우 간단한 템플릿 템플릿 매개 변수 예제 :
template <class T>
struct Tag1 { };
template <class T>
struct Tag2 { };
template <template <class> class Tag>
struct IntTag {
typedef Tag<int> type;
};
int main() {
IntTag<Tag1>::type t;
}
#include <vector>
#include <iostream>
template <class T, template <class...> class C, class U>
C<T> cast_all(const C<U> &c) {
C<T> result(c.begin(), c.end());
return result;
}
int main() {
std::vector<float> vf = {1.2, 2.6, 3.7};
auto vi = cast_all<int>(vf);
for(auto &&i: vi) {
std::cout << i << std::endl;
}
}
자동으로 non-type 템플릿 인자 선언하기
C ++ 17 이전에는 템플릿이 아닌 형식 매개 변수를 작성할 때 형식을 먼저 지정해야했습니다. 따라서 일반적인 패턴은 다음과 같이 작성됩니다.
template <class T, T N>
struct integral_constant {
using type = T;
static constexpr T value = N;
};
using five = integral_constant<int, 5>;
그러나 복잡한 표현식의 경우 이와 같은 것을 사용하려면 템플릿을 인스턴스화 할 때 decltype(expr), expr
을 써야합니다. 해결책은이 관용구를 단순화하고 auto
허용하는 것입니다.
template <auto N>
struct integral_constant {
using type = decltype(N);
static constexpr type value = N;
};
using five = integral_constant<5>;
unique_ptr에 대한 빈 사용자 정의 삭제 자
좋은 동기 부여 예제는 빈 기본 최적화와 unique_ptr
대한 커스텀 deleter를 결합하는 것으로부터 올 수있다. 다른 C API deleter는 리턴 타입이 다르지만, 우리는 상관하지 않는다.
template <auto DeleteFn>
struct FunctionDeleter {
template <class T>
void operator()(T* ptr) const {
DeleteFn(ptr);
}
};
template <T, auto DeleteFn>
using unique_ptr_deleter = std::unique_ptr<T, FunctionDeleter<DeleteFn>>;
그리고 이제는 타입 T
의 인자를 취할 수있는 함수 포인터를 리턴 타입에 관계없이 템플릿 비 - 타입 파라미터로 사용할 수 있으며, 그 unique_ptr
아무런 크기의 오버 헤드가없는 unique_ptr
얻을 수 있습니다 :
unique_ptr_deleter<std::FILE, std::fclose> p;
형식이 아닌 템플릿 매개 변수
템플릿 매개 변수와 같은 유형 외에도 다음 기준 중 하나를 충족시키는 상수 표현식의 값을 선언 할 수 있습니다.
- 정수형 또는 열거 형,
- 객체에 대한 포인터 또는 함수에 대한 포인터,
- lvalue 객체에 대한 참조 또는 함수에 대한 lvalue 참조,
- 멤버에 대한 포인터,
-
std::nullptr_t
.
모든 템플릿 매개 변수와 마찬가지로 형식이 아닌 템플릿 매개 변수는 템플릿 인수 공제를 통해 명시 적으로 지정되거나, 기본값으로 설정되거나, 암시 적으로 파생 될 수 있습니다.
유형이 아닌 템플릿 매개 변수 사용의 예 :
#include <iostream>
template<typename T, std::size_t size>
std::size_t size_of(T (&anArray)[size]) // Pass array by reference. Requires.
{ // an exact size. We allow all sizes
return size; // by using a template "size".
}
int main()
{
char anArrayOfChar[15];
std::cout << "anArrayOfChar: " << size_of(anArrayOfChar) << "\n";
int anArrayOfData[] = {1,2,3,4,5,6,7,8,9};
std::cout << "anArrayOfData: " << size_of(anArrayOfData) << "\n";
}
유형 및 비 유형 템플릿 매개 변수를 명시 적으로 지정하는 예 :
#include <array>
int main ()
{
std::array<int, 5> foo; // int is a type parameter, 5 is non-type
}
비 형식 템플릿 매개 변수는 템플릿 반복을 달성하는 방법 중 하나이며 메타 프로그래밍 을 수행 할 수 있습니다.
가변성 템플릿 데이터 구조
컴파일 타임에 정의되는 데이터 멤버의 수와 유형이 가변적 인 클래스 나 구조를 정의하는 것이 종종 유용합니다. 표준 예제는 std::tuple
이지만 때로는 자체 사용자 정의 구조를 정의해야합니다. 다음은 std::tuple
과 같이 상속이 아닌 복 합성을 사용하여 구조를 정의하는 예제입니다. 일반 (비어 있음) 정의로 시작하고 후기 전문 분야의 압출 종료를위한 기본 케이스로도 사용됩니다.
template<typename ... T>
struct DataStructure {};
이것으로 이미 빈 구조 인 DataStructure<> data
를 정의 할 수 있습니다.
다음은 재귀 케이스 전문화입니다.
template<typename T, typename ... Rest>
struct DataStructure<T, Rest ...>
{
DataStructure(const T& first, const Rest& ... rest)
: first(first)
, rest(rest...)
{}
T first;
DataStructure<Rest ... > rest;
};
이제 DataStructure<int, float, std::string> data(1, 2.1, "hello")
와 같은 임의의 데이터 구조를 만들면 충분합니다.
무슨 일 이니? 첫째, 팩 Rest
의 특정 메이크업을 신경 쓰지 않고 적어도 하나의 가변적 인 템플릿 매개 변수 (즉 위의 T
)가 존재한다는 요구 사항이있는 전문화입니다. T
가 있다는 것을 알면 데이터 멤버의 정의가 first
됩니다. 나머지 데이터는 DataStructure<Rest ... > rest
로 재귀 적으로 패키지화됩니다. 생성자는 rest
멤버에 대한 재귀 적 생성자 호출을 포함하여 두 멤버를 초기화합니다.
이를 더 잘 이해하기 위해 예제를 통해 작업 할 수 있습니다. DataStructure<int, float> data
선언 DataStructure<int, float> data
가정합니다. 선언은 먼저 특수화와 일치하여 int first
와 DataStructure<float> rest
데이터 멤버가있는 구조를 생성합니다. rest
정의는 다시이 전문화와 일치하며, float first
자체 float first
및 DataStructure<> rest
구성원을 작성합니다. 마지막으로이 마지막 rest
는 기본 케이스 정의와 일치하여 빈 구조를 만듭니다.
이것을 다음과 같이 시각화 할 수 있습니다 :
DataStructure<int, float>
-> int first
-> DataStructure<float> rest
-> float first
-> DataStructure<> rest
-> (empty)
이제 데이터 구조가 DataStructure<int, float, std::string> data
, 개별 데이터 요소에 쉽게 액세스 할 수 없기 때문에 (예를 들어 DataStructure<int, float, std::string> data
의 마지막 멤버에 액세스하려면 data.rest.rest.first
를 사용해야 data.rest.rest.first
, 정확히 사용자에게 친숙하지는 않음). 그래서 get
메소드를 추가합니다 (기본 케이스 구조는 get
데이터가 없으므로 전문화에만 필요함).
template<typename T, typename ... Rest>
struct DataStructure<T, Rest ...>
{
...
template<size_t idx>
auto get()
{
return GetHelper<idx, DataStructure<T,Rest...>>::get(*this);
}
...
};
보시다시피,이 get
멤버 함수 자체는 템플릿 화되어 있습니다. 이번에는 필요한 멤버의 인덱스를 참조하십시오. 따라서 data.get<1>()
는 std::tuple
과 비슷한 data.get<1>()
과 같은 data.get<1>()
수 있습니다. 실제 작업은 헬퍼 클래스 GetHelper
의 정적 함수에 의해 수행됩니다. DataStructure
의 get
에서 직접 필요한 기능을 정의 할 수없는 이유는 idx
를 전문화해야 할 필요가 있기 때문입니다 (그러나 곧 보게 될 것입니다)하지만 포함하는 클래스를 전문화하지 않고 템플릿 멤버 함수를 전문화 할 수는 없기 때문입니다 주형. 여기에 C ++ 14 스타일의 auto
를 사용하면 리턴 타입에 대해 꽤 복잡한 표현식이 필요하기 때문에 우리의 삶이 상당히 단순해진다는 점에 유의하십시오.
그래서 도우미 클래스에. 이번에는 빈 전달 선언과 두 가지 전문화가 필요할 것입니다. 먼저 선언문 :
template<size_t idx, typename T>
struct GetHelper;
이제 기본 경우 ( idx==0
때). 이 경우 first
멤버 만 반환합니다.
template<typename T, typename ... Rest>
struct GetHelper<0, DataStructure<T, Rest ... >>
{
static T get(DataStructure<T, Rest...>& data)
{
return data.first;
}
};
재귀적인 경우 idx
를 감소시키고 rest
구성원에 대해 GetHelper
를 호출합니다.
template<size_t idx, typename T, typename ... Rest>
struct GetHelper<idx, DataStructure<T, Rest ... >>
{
static auto get(DataStructure<T, Rest...>& data)
{
return GetHelper<idx-1, DataStructure<Rest ...>>::get(data.rest);
}
};
예를 통해 DataStructure<int, float> data
있고 data.get<1>()
필요 data.get<1>()
가정 해 보겠습니다. 이것은 GetHelper<0, DataStructure<float>>::get(data.rest)
를 차례로 호출하여 GetHelper<1, DataStructure<int, float>>::get(data)
(두 번째 특수화)를 호출합니다. ( idx
가 0 일 때 1 차 특수화로) data.rest.first
.
그래서 그게 다야! 다음은 main
함수에서 몇 가지 예제를 사용하여 전체 작동 코드입니다.
#include <iostream>
template<size_t idx, typename T>
struct GetHelper;
template<typename ... T>
struct DataStructure
{
};
template<typename T, typename ... Rest>
struct DataStructure<T, Rest ...>
{
DataStructure(const T& first, const Rest& ... rest)
: first(first)
, rest(rest...)
{}
T first;
DataStructure<Rest ... > rest;
template<size_t idx>
auto get()
{
return GetHelper<idx, DataStructure<T,Rest...>>::get(*this);
}
};
template<typename T, typename ... Rest>
struct GetHelper<0, DataStructure<T, Rest ... >>
{
static T get(DataStructure<T, Rest...>& data)
{
return data.first;
}
};
template<size_t idx, typename T, typename ... Rest>
struct GetHelper<idx, DataStructure<T, Rest ... >>
{
static auto get(DataStructure<T, Rest...>& data)
{
return GetHelper<idx-1, DataStructure<Rest ...>>::get(data.rest);
}
};
int main()
{
DataStructure<int, float, std::string> data(1, 2.1, "Hello");
std::cout << data.get<0>() << std::endl;
std::cout << data.get<1>() << std::endl;
std::cout << data.get<2>() << std::endl;
return 0;
}
명시 적 인스턴스화
명시 적 인스턴스화 정의는 템플릿을 사용하지 않고 구체적인 클래스, 함수 또는 변수를 만들고 선언합니다. 다른 번역 단위에서 명시 적 인스턴스화를 참조 할 수 있습니다. 이것은 유한 인수 세트로만 인스턴스화 될 경우 헤더 파일에 템플릿을 정의하는 것을 피하기 위해 사용할 수 있습니다. 예 :
// print_string.h
template <class T>
void print_string(const T* str);
// print_string.cpp
#include "print_string.h"
template void print_string(const char*);
template void print_string(const wchar_t*);
print_string<char>
및 print_string<wchar_t>
은 print_string.cpp
에서 명시 적으로 인스턴스화되기 때문에 링커는 print_string
템플리트가 헤더에 정의되지 않은 경우에도이를 찾을 수 있습니다. 이러한 명시 적 인스턴스화 선언이 없으면 링커 오류가 발생할 수 있습니다. 헤더 파일에 템플릿 만 구현할 수있는 이유는 무엇입니까?를 참조하십시오 .
명시 적 인스턴스화 정의 앞에 extern
키워드 가 있으면 명시 적 인스턴스화 선언이 됩니다. 특정 전문 분야에 대한 명시 적 인스턴스화 선언이 있으면 현재 번역 단위 내에서 해당 전문 분야의 암시 적 인스턴스화를 방지합니다. 대신 암시 적 인스턴스화를 야기하는 해당 전문 분야에 대한 참조는 동일하거나 다른 TU의 명시 적 인스턴스화 정의를 나타낼 수 있습니다.
foo.h
#ifndef FOO_H
#define FOO_H
template <class T> void foo(T x) {
// complicated implementation
}
#endif
foo.cpp
#include "foo.h"
// explicit instantiation definitions for common cases
template void foo(int);
template void foo(double);
main.cpp
#include "foo.h"
// we already know foo.cpp has explicit instantiation definitions for these
extern template void foo(double);
int main() {
foo(42); // instantiates foo<int> here;
// wasteful since foo.cpp provides an explicit instantiation already!
foo(3.14); // does not instantiate foo<double> here;
// uses instantiation of foo<double> in foo.cpp instead
}