수색…


소개

클래스, 함수 및 (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 가지 다른 의미를 갖는 키워드 입니다.

  1. <> 묶인 템플릿 매개 변수 목록이 나오면 클래스 템플릿 , 함수 템플릿 또는 기존 템플릿의 부분 전문화 와 같은 템플릿을 선언합니다.

    template <class T>
    void increment(T& x) { ++x; }
    
  2. 뒤에 <> 이 있으면 명시 적 (전체) 특성화를 선언합니다.

    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);
    }
    
  3. <> 없는 선언이 나올 경우 명시적인 인스턴스화 선언 또는 정의를 형성합니다.

    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
    
  4. 템플릿 매개 변수 목록 내에서, 그것은 도입 템플릿 템플릿 매개 변수를 .

    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));
        }
        // ...
    };
    
  5. 범위 분석 연산자 :: 및 클래스 멤버 액세스 연산자 뒤에 .-> 이면 다음 이름이 템플리트임을 지정합니다.

    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)

첫 번째 경우에 유형 TX ( 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;
}

별칭 템플릿

C ++ 11

기본 예 :

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;
}
C ++ 11
#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 허용하는 것입니다.

C ++ 17
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
}

비 형식 템플릿 매개 변수는 템플릿 반복을 달성하는 방법 중 하나이며 메타 프로그래밍 을 수행 할 수 있습니다.

가변성 템플릿 데이터 구조

C ++ 14

컴파일 타임에 정의되는 데이터 멤버의 수와 유형이 가변적 인 클래스 나 구조를 정의하는 것이 종종 유용합니다. 표준 예제는 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 firstDataStructure<float> rest 데이터 멤버가있는 구조를 생성합니다. rest 정의는 다시이 전문화와 일치하며, float first 자체 float firstDataStructure<> 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 의 정적 함수에 의해 수행됩니다. DataStructureget 에서 직접 필요한 기능을 정의 할 수없는 이유는 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 템플리트가 헤더에 정의되지 않은 경우에도이를 찾을 수 있습니다. 이러한 명시 적 인스턴스화 선언이 없으면 링커 오류가 발생할 수 있습니다. 헤더 파일에 템플릿 만 구현할 수있는 이유는 무엇입니까?를 참조하십시오 .

C ++ 11

명시 적 인스턴스화 정의 앞에 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
}


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow