2. Index What is a Template?
– Template
– Read Template
– using Template
function template
template class
using by C++11
Variadic Template
SFINAE & Partial Template Specialization
– SFINAE
– Partial Template Specialization
– auto & decltype
– Example : FixValue
Final Example : MFTE
– Example : MeasureFunctionTimeElapsed
3. What is a Template?
MSDN : https://msdn.microsoft.com/ko-kr/library/x5w1yety.aspx
4. TEMPLATE
Template이 뭔가요?
– 확정되지 않은 무언가를 사용하여 실제 사용될 때까지 실제 형태를 만들지 않는 방법
– 템플릿에서의 무언가는 자료형 일 수도, 값일 수도 있음
심지어 함수가 들어올 수도 있음
– 다른 언어에서 Generic Programming(일반적 프로그래밍) 라고 불림
4Present by Team Warp
5. TEMPLATE
Template의 특징
– Template을 특수화Specialization 하지 않으면 컴파일 시 코드에 포함 안 됨
특수화 : 무언가를 확정 시킴
컴파일 되는 template 코드는 특수화된 코드
template 코드 상태로 들어가는 것이 아님.
– Template은 컴파일 시점에서 확정되어야 함
값으로 들어오는 Template은 컴파일 시점 상수
– Template은 한 번에 여러 개 사용 가능
Template은 타입 또는 값에 대한 초기화를 할 수 있음
함수와 동일하게, 오른쪽부터 채워 넣어야 함
기본 타입 또는 값은 왼쪽의 Template 인수를 통하여 정할 수 있음
5Present by Team Warp
6. Read TEMPLATE
Template을 읽는 방법 (함수 템플릿의 예)
template<typename Ty = int>
void Swap (Ty& lhs, Ty& rhs)
{
Ty temp = lhs;
lhs = rhs;
rhs = temp;
}
┌ Template을 사용하는데
─ 무슨 타입인지는 모르겠지만 그 타입의 이름은 Ty고
템플릿 인수를 생략하면 int로 간주하겠다.
• template<>에서, <> 안에 들어가는 자료형 또는 값은
템플릿 인수라고 한다.
• 자료형 인수는 typename 또는 class 를 앞에 붙여
타입 이름이라는 것을 나타낸다.
• typename 과 class 의 차이는 없다.
6Present by Team Warp
7. Read TEMPLATE
Template을 읽는 방법 (함수 템플릿의 예)
int a = 10;
int b = 5;
Swap<int>(a, b); // a와 b로 Ty를 추론할 수 있으므로 <int> 생략 가능
┌ Swap의 첫 번째 템플릿 인수는 여기서 int로 하겠다.
• 템플릿 인수가 여러 개면 왼쪽부터
순서대로 입력한다.
• 호출 시 템플릿 인수를 명시하는 것을
명시적 템플릿 특수화라고 한다.
7Present by Team Warp
8. usE TEMPLATE
Template의 사용처는 크게 2가지로 나눈다.
1. 함수 템플릿
– 함수에서 템플릿을 사용하는 것
– 클래스 내부에서도 함수 템플릿 구현 가능
– 호출 시 매개인자로 템플릿 인수 추론이 가능하면 템플릿 인수를 생략할 수 있음
ex】 std::swap(…); std::invoke(…); std::make_unique(…)
2. 템플릿 클래스
– 클래스에서 템플릿을 사용하는 것
– 반드시 템플릿 인수를 입력해야 함
– 템플릿 클래스의 포인터는 같은 템플릿 인수를 사용한 템플릿 클래스의 주소만 받음
ex】 std::vector<>
8Present by Team Warp
9. function TEMPLATE
함수 템플릿은 언제 만들어요?
– 자료형이 무엇이든 함수 논리가 동일한 경우 = 범용적인 함수일 때
Swap 함수의 경우
– 크기를 모르는 배열을 참조하고 싶을 때
template<typename Ty, int N>
constexpr int GetArraySize (Ty(&arr)[N]) noexcept { return N;}
9Present by Team Warp
10. function TEMPLATE
함수 템플릿을 사용할 때 무엇을 주의해야 하나요?
– 무엇이든 템플릿 인수에 들어올 수 있지만 그것이 돌아간다고는 하지 않았다.
다행히도 컴파일 에러를 뿜는다.
하지만 에러 위치가 함수 호출 위치가 아니다
– 특정 자료형의 경우에 원하지 않은 결과가 도출될 수 있음
무엇인지 모르는 자료형 Ty1과 0이 아닌 자료형 Ty2를 인자로 받을 때 Ty1 / Ty2 하는 함수
12 / 5 의 결과로 2.4 를 받기를 원했지만 int / int 이기 때문에 결과는 2가 됨
템플릿 부분 특수화를 통하여 해결 가능
– 멤버 함수 템플릿의 경우, 상속이 불가능
자식 클래스에서 호출 가능
자식 클래스에서 호출하는 멤버 함수는 overriding 적용됨
반환형을 auto로 사용하면 함수 템플릿으로 판단
10Present by Team Warp
11. TEMPLATE class
템플릿 클래스는 언제 만들어요?
– C++에서, class와 struct의 차이는 기본 접근자가 private/public 인가의 차이
– template class 라고 하지만 template struct 도 포함
– 클래스의 멤버 변수(field)를 범용적으로 받고 싶을 때
자료구조에 유용, STL의 Container는 모두 class template
– 특정 조건에만 생성되게 하고 싶은 코드가 있을 때
FixValue의 예에서 추가적인 설명
11Present by Team Warp
12. TEMPLATE class
템플릿 클래스를 사용할 때 무엇을 주의해야 하나요?
– 템플릿 클래스는 템플릿 인수가 모두 같아야 동일한 타입이라고 판단
해결방법 : 클래스 템플릿의 Base class 구현. 기능이 없어도 됨
– 템플릿 클래스를 상속한 자식 템플릿 클래스는 부모의 템플릿 인수도 알아야 함
자료형 Ty를 템플릿 인수로 받는 부모 클래스를 상속할 경우
– Ty가 무슨 자료형 인지 직접 작성하거나
– 자식 클래스 역시 템플릿 클래스가 되어서 부모 클래스의 템플릿 인수를 자신도 받아야 함
– 함수 템플릿과 마찬가지로, 무엇이 들어올지 전혀 모르기 때문에 에러 발생 가능
12Present by Team Warp
13. using by C++11
MSDN : https://msdn.microsoft.com/ko-kr/library/dn467695.aspx
14. using
C++11의 using에 대해 잠깐 짚고 넘어갑시다.
– C++11 부터 using 별칭 선언이 가능
– 기존에 typedef 를 사용하여 작성하던 별칭 선언을 using 키워드로도 되게 함
– 사용법 : using MyT = int;
– 장점 :
1. 가독성 향상
– 배열의 별칭 선언 (동일하게 크기 4인 int형 배열의 별칭 선언)
2. 템플릿 사용 가능
┌ 별칭
└ 별칭의 실체
14Present by Team Warp
typedef int int4[4];
using int4 = int[4];
15. using
using template에 대해 조금 알아봅시다.
– 범용적인 별칭 선언이 가능
예를 들어, 크기가 N인 배열을 만들고 싶을 경우(함수의 인자로 배열(의 시작주소)을 요구할 때)
template <typename Ty> using TyArr = Ty[];
– TyArr<int>{ 1, 2, 3, 4 }; 는 int arr[]{1,2,3,4}; 와 동일한 코드
– 예시 )
void SetColor4f(float* color); // color는 float[4]의 시작주소
…
SetColor4f( TyArr<float>{ 0.5f, 0.3f, 1.f, 1.f } );
불필요한 변수를 만들지 않고 생성과 즉시 인자로 줄 수 있음
15Present by Team Warp
16. Variadic Template
MSDN : https://msdn.microsoft.com/ko-kr/library/dn439779.aspx
BLOG : http://lusain.egloos.com/3158201
17. variadic template
가변 인자 템플릿이 뭐에요?
– C++11 부터 사용 가능
– 템플릿 인수의 개수가 고정되지 않은 템플릿 인수 묶음
C의 printf 처럼 템플릿 인수를 가변적으로 넣을 수 있음
가변 인자 템플릿은 하나의 템플릿 인수로 인식
가변 인자 템플릿 타입의 템플릿 인수는 매개변수의 가장 오른쪽에만 가능
– 사용법 :
template<typename Fn, typename... Args>
void invokeFunc(Fn&& func , Args&&... args)
{
func(args...);
}
┌ typename... 으로 가변 인자 템플릿이라고 표시
└ 가변 인자 선언은 ...args 같이 인자 앞에 ...을 붙임
이 매개변수는 매개변수 팩이라고 부름
└ 가변 인자 사용은 args... 같이 인자 뒤에 ...을 붙임
args... 같은 표현을 팩 확장이라고 부름
17Present by Team Warp
18. variadic template
가변 인자 템플릿은 어디에 사용해요?
– 가변인자는 다른 함수를 호출할 때의 매개인자 목록으로 사용 가능
void func(int a, int b, int c);
…
invokeFunc(func, 1, 2, 3); // 내부에서 func(1,2,3); 호출
– 가변인자와 템플릿 인수로 가변 인자 템플릿의 첫 번째 인자를 뺄 수 있음
template<typename Ty, typename... Args>
void print(Ty ty, Args... args)
{
printType(ty); // 가변 인자 템플릿의 첫 번째 인자 처리
print(args...);
}
가변 인자 템플릿의 첫 번째 인자 ┐ ┌ 첫 번째 인자를 제외한 가변 인자 템플릿
18Present by Team Warp
19. variadic template
가변 인자 템플릿 오버로딩
– 가변인자가 하나도 남지 않게 될 경우를 위한 함수 오버로딩이 필요
void print(); // sizeof...(args) == 0 일 때
// 필요한 타입으로의 캐스팅
void printType(int i);
void printType(string s);
...
template<typename Ty, typename... Args>
void print(Ty ty, Args... args)
{
printType(ty); // 가변 인자 템플릿의 첫 번째 인자 처리
print(args...);
}
sizeof...(Args); 으로 매개변수 팩에 포함된
템플릿 인수 개수를 알 수 있다.
19Present by Team Warp
20. Substitution Failure Is Not An Error
Partial Template Specialization
MSDN : https://msdn.microsoft.com/ko-kr/library/3967w96f.aspx
GitHub: https://github.com/jwvg0425/ModernCppStudy/wiki/SFINAE
BLOG : http://lusain.egloos.com/3161710
20
21. SFINAE
치환 실패는 오류가 아닙니다 Substitution Failure Is Not An Error?
– 템플릿 인수가 틀리더라도, 다른 템플릿에서 알맞은 치환이 있을 수 있음
예를 들어, sum(Ty a, Ty b)에서, Ty에 Player 라는 class가 들어갈 수 있음
template<typename Ty> auto sum(Ty a, Ty b) { return {a + b}; }
Player는 + 연산자 오버로딩이 되어있지 않음
템플릿 인수가 Player 라면 sum은 정상적으로 동작하지 않음 = 컴파일 에러
21Present by Team Warp
22. SFINAE
치환 실패는 오류가 아닙니다 Substitution Failure Is Not An Error?
이 때, 다음과 같은 함수가 있다면 정상적으로 동작
( Player는 int id()라는 멤버함수를 가졌다고 가정 )
template<> auto sum(Player a, Player b) { return {a.id() + b.id()}; }
– 이렇게 일부 템플릿 인수를 확정시킨 것을 템플릿 부분 특수화라고 함
22Present by Team Warp
23. Partial Template Specialization
템플릿 부분 특수화 Partial Template Specialization?
– 템플릿을 이용한 클래스, 함수 등에서 일부 템플릿을 특정하여 오버로드 하는 기법
template<typename Ty1,typename Ty2> auto sum(Ty1 a, Ty2 b);
이라는 함수가 있을 때,
template<typename Ty> auto sum(int a, Ty b);
template<typename Ty> auto sum(Ty a, int b);
같이 일부 템플릿을 특정(Ty1 또는 Ty2를 int)
23Present by Team Warp
24. AUTO & decltype
쉬는 시간 : decltype이 뭐에요?
– decltype
식을 decltype ( ) 키워드 안에 넣으면 식의 반환 타입을 추론하여 그 타입을 사용
템플릿 프로그래밍에 유용하게 사용
사용 예 :
int i = 10;
float f = 5.f;
decltype(i + f) p; // p의 type은 i + f와 동일한 타입. 즉 float
24Present by Team Warp
25. AUTO & decltype
쉬는 시간 : auto가 뭐에요?
– auto
C++11에서 바뀐 기능(이전까지는 지역 변수임을 명시하는 쓸모 없는 키워드였음)
– 크게 2가지의 사용처가 있으며, C++14에서 하나 더 추가됨
변수로써 사용될 때
– 초기값이 반드시 존재해야 함
– 초기값의 타입을 알아서 잘 추론해서 타입을 컴파일 시점에 결정
대체로 잘 추론하지만 가끔 원하지 않는 타입으로 추론할 때가 있기 때문에 사용에 주의
– 사용 예 :
int i = 5;
auto p = i; // p의 type은 int, 값은 5
25Present by Team Warp
26. AUTO & decltype
쉬는 시간 : auto가 뭐에요?
– auto
선언 시 초기화 값에서 const, volatile 등의 선언자가 모두 제거된 타입을 추론
* 또는 & 등의 참조자를 붙일 수 있음. 이 경우에도 알아서 잘 추론해줌
함수 포인터, lambda도 받을 수 있음
26Present by Team Warp
27. AUTO & decltype
쉬는 시간 : auto가 뭐에요?
– auto
함수의 반환형으로도 사용
– 반환 타입 확정을 decltype으로 컴파일 시점까지 지연하는 용도
예:
template<class Ty1,class Ty2>
auto sum(Ty1 a, Ty2 b) -> decltype(a + b)
{
return a + b;
}
C++14에서는 decltype을 굳이 적지 않고도 return 문에서 추론
27Present by Team Warp
└ Ty1과 Ty2의 + 연산자의 반환 타입이 무엇인지
모르기 때문에 a + b를 해본 뒤
그 결과값의 타입을 반환 타입으로 쓰겠다는 뜻
28. AUTO & decltype
쉬는 시간 : auto가 뭐에요?
– auto
C++14부터 lambda의 매개인자 타입으로도 사용 가능
lambda는 템플릿 선언이 불가능했지만 auto 매개인자를 사용함으로써 템플릿 프로그래밍이 가능해짐
사용 예:
auto p = [](auto a, auto b) { return a + b; };
…
p(10, 1.5f); // 11.5f 반환
28Present by Team Warp
29. example : FixValue
FixValue : 정수를 받아 세 자리마다 쉼표(,)를 찍은 string 반환
– 함수 원본
– int 말고 다른 자료형도 받고 싶다!
– 그래, template을 써 보자!
string FixValue(int fixnum)
{
if (fixnum == 0) { return to_string(0); }
string szFix;
int count = 0;
while (fixnum > 0)
{
szFix += to_string(fixnum % 10);
if ((fixnum /= 10) == 0) continue;
if (++count % 4 != 3) continue;
szFix += ',';
++count;
}
reverse(szFix.begin(), szFix.end());
return szFix;
}
29Present by Team Warp
30. example : FixValue
FixValue : 정수를 받아 세 자리마다 쉼표(,)를 찍은 string 반환
– template의 문제점이 떠오름
엉뚱한 자료형이 올 수도 있음
– template을 포기할까?
template<typename Ty>
string FixValue(Ty fixnum)
{
if (fixnum == 0) { return to_string(0); }
string szFix;
int count = 0;
while (fixnum > 0)
{
szFix += to_string(fixnum % 10);
if ((fixnum /= 10) == 0) continue;
if (++count % 4 != 3) continue;
szFix += ',';
++count;
}
reverse(szFix.begin(), szFix.end());
return szFix;
}
30Present by Team Warp
31. example : FixValue
FixValue : 정수를 받아 세 자리마다 쉼표(,)를 찍은 string 반환
– 템플릿 부분 특수화를 이용해보자!
– SFixValue
선언으로 두 번째 인자의 기본값 결정
두 번째 인자로 정수 타입인지 확인
두 번째 인자에 따라 호출이 결정
– true일 때 정상 호출
– false일 때 에러 출력
template<typename Ty,
bool isInteger = std::is_integral<Ty>::value>
struct SFixValue;
template<typename Ty>
struct SFixValue<Ty, true>
{
template<typename Ty>
static string _Call(Ty fixnum) { ... } //진짜
}
template<typename Ty>
struct SFixValue<Ty, false>
{
template<typename Ty>
static string _Call(Ty fixnum) { ... } //Error
};
31Present by Team Warp
32. example : FixValue
FixValue : 정수를 받아 세 자리마다 쉼표(,)를 찍은 string 반환
– 함수 호출은 이렇게
– 함수 호출 시 컴파일 타임에 추론
– fixnum이 정수 형이면 함수 호출 성공
– 그 이외일 경우 : struct SFixValue<Ty, false> 에서 정의한 _Call 함수 호출
실패 시 코드 :
template<typename Ty>
inline string FixValue(Ty fixnum)
{
return SFixValue<Ty>::_Call(fixnum);
}
template<typename Ty> struct SFixValue<Ty, false>
{
template<typename Ty> static string _Call(Ty fixnum)
{
static_assert(false, "FixValue only use integer type!");
return string();
}
};
32Present by Team Warp
33. type traits
std::is_integral<Ty>::value 가 도대체 무슨 코드에요?
– C++11에서 형식 인수의 속성에 대한 정보를 제공하거나 변형된 형식을 생성하는
컴파일 타임 상수를 제공하는 템플릿을 정의하기 위한 헤더 제공
#include <type_traits>
템플릿 인수에 대해 컴파일 타임에 사용 가능한 상수를 반환
is_integral<Ty>의 경우, Ty가 정수 형인지 질의하는 구조체
– ::value로 컴파일 시점에 상수 bool 값을 반환
ex】is_void, is_base_of, is_integral… 등
33Present by Team Warp
35. example : Measure Fn Time Elapsed
– 함수의 실행시간을 측정하는 함수 구현이 목표
– 실행시간을 가공하는 함수, 측정 함수, 측정 함수의 인자(가변)를 매개인자로 받음
– 측정 함수의 반환값이 존재하면 MFTE가 반환하는 것으로 설계
– 실제 사용 예 :
bool CScene::Render(HDC hDC) { ... }
…
bool bRenderSucc = MeasureFunctionTimeElapsed(
[&](auto d) {
auto du = chrono::duration_cast<chrono::nanoseconds>(d).count();
MessageBox(nullptr, to_wstring(du).c_str(), "", MB_OK);
}
, &CScene::Render, hDC);
35Present by Team Warp
가공함수는 모든 함수마다
다르므로 람다로 구현 ──
┌Render의 반환값을 MFTE가 대신 받음
└Render 함수와 매개인자. 클래스 멤버 함수도 쓸 수 있다.
• 필요한 헤더
#include <chrono>
#include <type_traits>
36. example : Measure Fn Time Elapsed
– 함수 구현
– 현재 시간을 기록하고 함수를 실행한 뒤 경과시간을 체크하는 방식
– 반환값을 저장하기 위해 auto retval 을 사용하여 함수 반환값을 저장 후 반환
– 근데 void 형은 auto로 못 받는데?
36Present by Team Warp
template<class _Callable, class _OutputFn, class... _Types>
static auto _Call(_Callable&& _Obj, _OutputFn&& _Out, _Types&&... _Args)
{
auto startTime = std::chrono::high_resolution_clock::now();
auto retval = std::invoke(
std::forward<_Callable>(_Obj), std::forward<_Types>(_Args)...);
auto du = std::chrono::high_resolution_clock::now() - (startTime);
std::invoke(std::forward<_OutputFn>(_Out), du);
return retval;
}
└ 보라색 배경색이 칠해진 부분은 시간 측정과 처리 부분
37. example : Measure Fn Time Elapsed
– 함수 구현
– 템플릿 부분 특수화로 void 반환형 용 함수를 만들자!
37Present by Team Warp
template<class _Callable, class _OutputFn, class... _Types>
static auto _Call(_Callable&& _Obj, _OutputFn&& _Out, _Types&&... _Args)
{
auto startTime = std::chrono::high_resolution_clock::now();
std::invoke(std::forward<_Callable>(_Obj), std::forward<_Types>(_Args)...);
auto du = std::chrono::high_resolution_clock::now() - (startTime);
std::invoke(std::forward<_OutputFn>(_Out), du);
}
└ 보라색 배경색이 칠해진 부분은 시간 측정과 처리 부분
38. example : Measure Fn Time Elapsed
– 템플릿 부분 특수화
38Present by Team Warp
template<
typename returnType
, bool _Is_void = is_void<returnType>::value
>
struct InvokerMeasureFunc;
template<typename Ty>
struct InvokerMeasureFunc<Ty, true>
: function_invoke_no_return_for_measure_time_elapsed { };
template<typename Ty>
struct InvokerMeasureFunc<Ty, false>
: function_invoke_return_late_for_measure_time_elapsed { };
┌void 반환 함수에 대한 MFTE를 가진 구조체
┌void 이외의 반환 함수에 대한 MFTE를 가진 구조체
└ 반환형이 void 인지 판단하는 형식 특질(type traits)
39. example : Measure Fn Time Elapsed
– 함수 본문
39Present by Team Warp
template<typename FnOutput, typename FnFunc, typename... Args> inline
constexpr auto MeasureFunctionTimeElapsed(FnOutput&& outputFunc, FnFunc&& Function,
Args&&... args) noexcept
{
return (InvokerMeasureFunc<decltype(std::invoke(std::forward<FnFunc>(Function)
, std::forward<Args>(args)...) )>
::_Call( std::forward<FnFunc>(Function),
std::forward<FnOutput>(outputFunc), std::forward<Args>(args)...)
);
}
┌갈색 배경색이 칠해진 부분은 함수의 returnType을
구하기 위한 부분
40. Q & A
E-Mail : sain9212@gmail.com
Twitter : @Lusain_Kim