[C++ 프로그래밍 / 이론] Chapter7. 템플릿

YH·2023년 7월 27일
0

Chapter7. 템플릿

템플릿은 C++의 일반화된 코드를 만드는 강력한 도구이다. 대상에 대한 타입만 다르고 로직이 다르지 않다면 템플릿을 이용해 단순한 반복 작업복사 & 붙여넣기(템플릿의 본질은 프로그래머가 수행해야 할 복사 & 붙여넣기 작업을 컴파일러가 타입 추론을 통해 컴파일 타임에 대신 해주는 것)를 컴파일러가 대신하도록 할 수 있다. 템플릿을 이용해 템플릿 클래스 또는 템플릿 함수를 만들 수 있는데, 기본적인 구조는 다음과 같다.

//클래스 템플릿
template<typename T>
class Sample {
	Sample(const Sample<T>& src) = default; //const Sample<T>&는 파라미터 타입의 예시
    //...
};

//함수 템플릿
template<typename T>
void f(const T& param); //const T&는 파라미터 타입의 예시

T는 아직 정해지지 않는 타입을 나타내며 T가 무엇인지는 템플릿 클래스의 인스턴스화 구문을 컴파일하거나 템플릿 함수의 호출 구문을 컴파일하는 순간 일련의 알고리즘에 의해 연역된다. 이 알고리즘은 auto의 타입 추론과 유사하다. auto는 초기화에 사용한 표현식으로부터 하나의 타입을 추론해내지만, 템플릿은 주어진 표현식으로부터 typename과 파라미터 타입을 함께 추론한다.
템플릿 클래스나 템플릿 함수가 인스턴스화 되는 시점에 컴파일러는 그 구현부를 볼 수 있어야 하므로 템플릿의 정의는 헤더 파일에 들어가야만 한다. 관례적으로 이렇게 템플릿 정의가 포함된 헤더파일의 확장자를 hpp로 사용한다.


  • 템플릿 파라미터 팩, 논타입 템플릿 파라미터, 템플릿 템플릿 파라미터

템플릿 파라미터는 한 번에 여러 개의 파라미터가 올 수 있고, 템플릿 파라미터 팩을 이용하면 무한한 개수의 파라미터를 정의할 수도 있다.

#include <cstdio>
#include <chrono>

template<typename Func, typename... Params>
auto runAndReturnElapsedTime(Func&& function, Params&&... params)
{
	auto start = std::chrono::high_resolution_clock::now();
    std::forward<decltype(function)>(function)(std::forward<decltype(params)>(params)...);
    return (std::chrono::high_resolution_clock::now() - start).count();
}

auto runAndReturnElapsedTime2 = [](auto&& function, auto&&... params)
{
	auto start = std::chrono::high_resolution_clock::now();
    std::forward<decltype(function)>(function)(std::forward<decltype(params)>(params)...);
    return (std::chrono::high_resolution_clock::now() - start).count();
}

int main()
{
	auto times = runAndReturnElapsedTime(printf, "%d %d\n", 1, 2);
    printf("%lld ns\n", times);
    
    auto times2 = runAndReturnElapsedTime2(printf, "%d %d\n", 1, 2);
    printf("%lld ns\n", times);
}

템플릿 파라미터에는 정수 타입 값, 열거형 타입 값, 포인터 값, 참조형 변수 등이 올 수도 있는데 이를 non-type 템플릿 파라미터라고 한다. non-type 템플릿 파라미터는 디폴트 값을 줄 수도 있다. C스타일의 배열을 래핑하고 있는 Array 클래스가 non-type 템플릿 파라미터를 활용해 정의되어 있다.

template <class Ty, std::size_t N>
class array;

템플릿 파라미터에 다른 템플릿이 올 수도 있다. 이를 템플릿 템플릿 파라미터라고 한다.

#include <iostream>
#include <vector>

using namespace std;

template<
	typename T,
    template<typename E, typename Allocator = allocator<E>> class Container
>
class Grid {
	size_t mWidth;
    size_t mHeight;
    Container<T>* mCells;
public:
	Grid<T, Container>(size_t inWidth, size_t inHeight) : mWidth(inWidth), mHeight(inHeight)
    {
    	mCells = new Container<T>[mWidth];
        for (size_t i = 0; i < mWidth; ++i)
        	mCells[i].resize(mHeight);
    }
    ~Grid()
    {
    	delete[] mCells;
    }
};

int main()
{
	Grid<int, vector> vGrid(5, 5);
}

using을 이용해 별칭 템플릿을 만들 수도 있다. typedef를 사용한다면 타입이 결정된 템플릿 인스턴스에 대해서만 별칭을 만들 수 있고 템플릿 자체에 대한 별칭을 만들 수 없다.

template<typename T>
using MyAllocList = std::list<T, MyAlloc<T>>;
MyAllocList<Widget> lw; //std::list<Widget, MyAlloc<Widget>>과 같음
typedef std::list<T, MyAlloc<T>> MyAllocList2; //컴파일 에러: T가 결정되어야 함
profile
Keep Recycling Your Dreams

0개의 댓글