Top Banner
Template at C++ KPU Lusain. Kim
41

Template at c++

Apr 16, 2017

Download

Education

Lusain Kim
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Template at c++

Template at C++

KPULusain. Kim

Page 2: Template at c++

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

Page 3: Template at c++

What is a Template?

MSDN : https://msdn.microsoft.com/ko-kr/library/x5w1yety.aspx

Page 4: Template at c++

TEMPLATE

Template이 뭔가요?

– 확정되지 않은 무언가를 사용하여 실제 사용될 때까지 실제 형태를 만들지 않는 방법

– 템플릿에서의 무언가는 자료형 일 수도, 값일 수도 있음

심지어 함수가 들어올 수도 있음

– 다른 언어에서 Generic Programming(일반적 프로그래밍) 라고 불림

4Present by Team Warp

Page 5: Template at c++

TEMPLATE

Template의 특징– Template을 특수화Specialization 하지 않으면 컴파일 시 코드에 포함 안 됨

특수화 : 무언가를 확정 시킴

컴파일 되는 template 코드는 특수화된 코드

template 코드 상태로 들어가는 것이 아님.

– Template은 컴파일 시점에서 확정되어야 함 값으로 들어오는 Template은 컴파일 시점 상수

– Template은 한 번에 여러 개 사용 가능 Template은 타입 또는 값에 대한 초기화를 할 수 있음

함수와 동일하게, 오른쪽부터 채워 넣어야 함

기본 타입 또는 값은 왼쪽의 Template 인수를 통하여 정할 수 있음

5Present by Team Warp

Page 6: Template at c++

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

Page 7: Template at c++

Read TEMPLATE

Template을 읽는 방법 (함수 템플릿의 예)

int a = 10;

int b = 5;

Swap<int>(a, b); // a와 b로 Ty를 추론할 수 있으므로 <int> 생략 가능

┌ Swap의 첫 번째 템플릿 인수는 여기서 int로 하겠다.

• 템플릿 인수가 여러 개면 왼쪽부터순서대로 입력한다.

• 호출 시 템플릿 인수를 명시하는 것을명시적 템플릿 특수화라고 한다.

7Present by Team Warp

Page 8: Template at c++

usE TEMPLATE

Template의 사용처는 크게 2가지로 나눈다.

1. 함수 템플릿

– 함수에서 템플릿을 사용하는 것

– 클래스 내부에서도 함수 템플릿 구현 가능– 호출 시 매개인자로 템플릿 인수 추론이 가능하면 템플릿 인수를 생략할 수 있음

ex】 std::swap(…); std::invoke(…); std::make_unique(…)

2. 템플릿 클래스– 클래스에서 템플릿을 사용하는 것– 반드시 템플릿 인수를 입력해야 함– 템플릿 클래스의 포인터는 같은 템플릿 인수를 사용한 템플릿 클래스의 주소만 받음

ex】 std::vector<>

8Present by Team Warp

Page 9: Template at c++

function TEMPLATE

함수 템플릿은 언제 만들어요?

– 자료형이 무엇이든 함수 논리가 동일한 경우 = 범용적인 함수일 때 Swap 함수의 경우

– 크기를 모르는 배열을 참조하고 싶을 때

template<typename Ty, int N>

constexpr int GetArraySize (Ty(&arr)[N]) noexcept { return N;}

9Present by Team Warp

Page 10: Template at c++

function TEMPLATE

함수 템플릿을 사용할 때 무엇을 주의해야 하나요?

– 무엇이든 템플릿 인수에 들어올 수 있지만 그것이 돌아간다고는 하지 않았다. 다행히도 컴파일 에러를 뿜는다. 하지만 에러 위치가 함수 호출 위치가 아니다

– 특정 자료형의 경우에 원하지 않은 결과가 도출될 수 있음 무엇인지 모르는 자료형 Ty1과 0이 아닌 자료형 Ty2를 인자로 받을 때 Ty1 / Ty2 하는 함수

12 / 5 의 결과로 2.4 를 받기를 원했지만 int / int 이기 때문에 결과는 2가 됨

템플릿 부분 특수화를 통하여 해결 가능

– 멤버 함수 템플릿의 경우, 상속이 불가능 자식 클래스에서 호출 가능 자식 클래스에서 호출하는 멤버 함수는 overriding 적용됨 반환형을 auto로 사용하면 함수 템플릿으로 판단

10Present by Team Warp

Page 11: Template at c++

TEMPLATE class

템플릿 클래스는 언제 만들어요?

– C++에서, class와 struct의 차이는 기본 접근자가 private/public 인가의 차이– template class 라고 하지만 template struct 도 포함

– 클래스의 멤버 변수(field)를 범용적으로 받고 싶을 때 자료구조에 유용, STL의 Container는 모두 class template

– 특정 조건에만 생성되게 하고 싶은 코드가 있을 때 FixValue의 예에서 추가적인 설명

11Present by Team Warp

Page 12: Template at c++

TEMPLATE class

템플릿 클래스를 사용할 때 무엇을 주의해야 하나요?

– 템플릿 클래스는 템플릿 인수가 모두 같아야 동일한 타입이라고 판단 해결방법 : 클래스 템플릿의 Base class 구현. 기능이 없어도 됨

– 템플릿 클래스를 상속한 자식 템플릿 클래스는 부모의 템플릿 인수도 알아야 함 자료형 Ty를 템플릿 인수로 받는 부모 클래스를 상속할 경우

– Ty가 무슨 자료형 인지 직접 작성하거나– 자식 클래스 역시 템플릿 클래스가 되어서 부모 클래스의 템플릿 인수를 자신도 받아야 함

– 함수 템플릿과 마찬가지로, 무엇이 들어올지 전혀 모르기 때문에 에러 발생 가능

12Present by Team Warp

Page 13: Template at c++

using by C++11

MSDN : https://msdn.microsoft.com/ko-kr/library/dn467695.aspx

Page 14: Template at c++

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];

Page 15: Template at c++

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

Page 16: Template at c++

Variadic Template

MSDN : https://msdn.microsoft.com/ko-kr/library/dn439779.aspxBLOG : http://lusain.egloos.com/3158201

Page 17: Template at c++

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

Page 18: Template at c++

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

Page 19: Template at c++

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

Page 20: Template at c++

Substitution Failure Is Not An ErrorPartial Template Specialization

MSDN : https://msdn.microsoft.com/ko-kr/library/3967w96f.aspxGitHub: https://github.com/jwvg0425/ModernCppStudy/wiki/SFINAEBLOG : http://lusain.egloos.com/3161710

20

Page 21: Template at c++

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

Page 22: Template at c++

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

Page 23: Template at c++

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

Page 24: Template at c++

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

Page 25: Template at c++

AUTO & decltype

쉬는 시간 : auto가 뭐에요?

– auto

C++11에서 바뀐 기능(이전까지는 지역 변수임을 명시하는 쓸모 없는 키워드였음)

– 크게 2가지의 사용처가 있으며, C++14에서 하나 더 추가됨

변수로써 사용될 때

– 초기값이반드시 존재해야 함

– 초기값의 타입을 알아서 잘 추론해서 타입을 컴파일 시점에 결정 대체로 잘 추론하지만 가끔 원하지 않는 타입으로 추론할 때가 있기 때문에 사용에 주의

– 사용 예 :

int i = 5;auto p = i; // p의 type은 int, 값은 5

25Present by Team Warp

Page 26: Template at c++

AUTO & decltype

쉬는 시간 : auto가 뭐에요?

– auto

선언 시 초기화 값에서 const, volatile 등의 선언자가 모두 제거된 타입을 추론

* 또는 & 등의 참조자를 붙일 수 있음. 이 경우에도 알아서 잘 추론해줌

함수 포인터, lambda도 받을 수 있음

26Present by Team Warp

Page 27: Template at c++

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를 해본 뒤그 결과값의 타입을 반환 타입으로 쓰겠다는 뜻

Page 28: Template at c++

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

Page 29: Template at c++

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

Page 30: Template at c++

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

Page 31: Template at c++

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

Page 32: Template at c++

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

Page 33: Template at c++

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

Page 34: Template at c++

Final Example

Measure Function Time Elapsed

Page 35: Template at c++

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>

Page 36: Template at c++

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;

}└ 보라색 배경색이 칠해진 부분은 시간 측정과 처리 부분

Page 37: Template at c++

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);

} └ 보라색 배경색이 칠해진 부분은 시간 측정과 처리 부분

Page 38: Template at c++

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)

Page 39: Template at c++

example : Measure Fn Time Elapsed

– 함수 본문

39Present by Team Warp

template<typename FnOutput, typename FnFunc, typename... Args> inlineconstexpr 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을구하기 위한 부분

Page 40: Template at c++

Q & A

E-Mail : [email protected] : @Lusain_Kim

Page 41: Template at c++

Thank You!