본 글은 코드누리의 Template Programming 강좌를 개인 학습 목적으로 정리한 글 입니다.
https://www.ecourse.co.kr/course-status/
템플릿
- 템플릿은 함수를 만드는 틀
- 특정 함수에 대해 많은 함수 오버로딩이 필요한 경우 유용하다.
템플릿 인스턴스화
- 템플릿은 말 그대로 함수를 만드는 틀이기 때문에 그 자체는 함수가 아니다.
- 클래스처럼 필요할 때 인스턴스를 만드는 개념
- 이런 인스턴스는 명시적 인스턴스화, 암시적 인스턴스화로 나뉜다.
- 암시적 인스턴스화는 통상 평소에 사용하는 것처럼 square<int> a; 이런 식으로 사용하는 것
- 명시적 인스턴스화는 사용하기 전에 위에서 사용할 것이라고 미리 선언해준다.
암시적 인스턴스화
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
using namespace boost::typeindex;
template<typename T>
T square(T a)
{
return a * a;
}
int main()
{
square<int>(3);
square(3.4);
}
- 함수 템플릿은 사용할 때 템플릿 매개변수 생략이 가능하다.
- 클래스 템플릿은 C++17부터 생성자를 통해 타입을 추론하므로 템플릿 매개변수 생략이 가능함
명시적 인스턴스화
template<typename T>
T square(T a)
{
return a * a;
}
template int square<int>(int);
template int square(int); // 이렇게 생략해도 사용이 가능
- template int 함수명<타입>(타입) 이렇게 위에서 미리 선언해두면 알아서 사용함
template<typename T>
class Test
{
public:
void foo() {}
void goo() {}
};
template class Test<int>; // foo, goo 모두 인스턴스화
template void Test<int>::foo() // foo만 인스턴스화
- 클래스 템플릿의 사용 예시
- 클래스의 경우 사용하지 않은 멤버 함수는 인스턴스화되지 않는다
템플릿 타입 디덕션
#include <iostream>
template<typename T>
class Vector
{
T* buff;
public:
Vector() {}
Vector(int sz, T initValue) {}
};
int main()
{
Vector<int> v1(10, 3); // 10개를 3으로 초기화
Vector v2(10, 3); // C++17부터 지원
}
- C++17부터 생성자의 인자를 통해 타입을 결정함
- 그런데 아래와 같이 타입을 추론할 수 있는 단서를 주지 않으면 에러가 난다.
int main()
{
Vector v3;
}
- 이 경우에는 디덕션 가이드를 만들어줘야 한다.
- 이런 생성자를 부를 때 인자가 없으면 이 가이드를 따르라~ 식의 해석이 가능할듯
Vector()->Vector<int>;
- Vector() 생성자가 불릴 때는 특별한 지시가 없다면 Vector<int>로 처리하라는 뜻
- 이 방식도 C++17부터 지원하며 권장하는 방식은 아님
- 실질적인 사용 예는 아래와 같다
#include <iostream>
#include <list>
template<typename T>
class Vector
{
T* buff;
public:
Vector() {}
Vector(int sz, T initValue) {}
template<typename C>
Vector(C& c) {}
};
// 다른 템플릿의 종류로 복사생성될 때 그 템플릿의 타입으로 생성해달라는 것
template<typename C>
Vector(C& c)->Vector<typename C::value_type>;
int main()
{
std::list l{ 1,2,3 };
Vector v3(l);
}
- 벡터의 타입을 리스트의 타입으로 맞추고 싶은데 typename C로 들어오는 인자는 list<int>이므로
- list의 타입인 int를 추출해내기 위한 방법의 일환
템플릿을 사용할 때 주의할 점
- 템플릿은 다른 기존 함수처럼 함수의 선언만 헤더에 두면 안됨
- 컴파일러가 함수 인스턴스를 만들 때 헤더 파일을 참조하기 때문
- 그래서 헤더 쪽에 구현까지 다 포함시켜야 함
- 마찬가지의 이유로 클래스 템플릿의 멤버 함수도 마찬가지로 바깥으로 뽑으면 안됨
지연된 인스턴스화
- 클래스 템플릿의 멤버 함수에서 int형을 역참조하는 경우 원래 에러가 나야 하는데 에러가 나지 않음
- 사용하지 않아서 인스턴스화 되지 않았기 때문
- 실제 사용되지 않으면 C++ 코드로 만들어지지 않는다.
- 이걸 지연된 인스턴스화라고 한다.
- 실제로 사용하면 그 때 에러가 발생함
- 클래스 템플릿의 static 멤버 변수는 외부선언할 때 template<typename T>를 붙여줘야 함
- 클래스템플릿의 static 멤버 변수는 실제 사용되지 않으면 인스턴스화되지 않음
#include <iostream>
class Resource1
{
public:
Resource1() { std::cout << "Resource1()" << std::endl; }
~Resource1() { std::cout << "~Resource1()" << std::endl; }
};
class Resource2
{
public:
Resource2(){std::cout << "Resource2()" << std::endl;}
~Resource2(){std::cout << "~Resource2()" << std::endl;}
};
template<typename T> struct Test
{
Resource1 res1;
static Resource2 res2;
};
template<typename T> Resource2 Test<T>::res2;
int main()
{
std::cout << "Main" << std::endl;
Test<int> t;
}
Main
Resource1()
~Resource1()
- 위에서 언급했듯 실제 사용되지 않아서 초기화되지 않았음
int main()
{
std::cout << "Main" << std::endl;
Test<int> t;
t.res2;
}
- 메인에 이렇게 사용하는 코드를 추가하면 그제서야 만들어짐
Resource2()
Main
Resource1()
~Resource1()
~Resource2()
if문과 지연된 인스턴스화
#include <iostream>
template<typename T> void foo(T n)
{
*n = 10;
}
int main()
{
if (false)
foo(0);
}
- if문은 실행시간 조건문이라 템플릿 함수 안에 if(false)로 놓고 if문 안에 에러가 발생하는 코드를 집어넣어도
- 런타임에 사용될 수 있다고 여겨서 인스턴스화되고, 결과적으로 에러가 발생한다.
if constexpr (C++17)
#include <iostream>
template<typename T> void foo(T n)
{
*n = 10;
}
int main()
{
if constexpr (false)
foo(0);
}
- constexpr은 이 if문이 실행될것인지를 컴파일시간에 판별한다.
- 그래서 위 문법은 에러가 나지 않는다.
'C++ > [ecourse] C++ Template' 카테고리의 다른 글
6. 템플릿 특수화 (1) | 2022.09.29 |
---|---|
5. 템플릿 기본 문법 - typename, template (0) | 2022.09.28 |
4. 템플릿 기본 문법 - 클래스 템플릿 (0) | 2022.09.27 |
3. 배열과 템플릿 (0) | 2022.09.26 |
2. Template Type Deduction (0) | 2022.09.26 |
댓글