Notice
Recent Posts
Recent Comments
Link
- 책_곽용재님 홈페이지
- 책_노란북 - 책 가격비교
- 책_김재우-SICP번역
- 플밍_쏘쓰포지
- 플밍_CodingHorror ?
- 플밍_상킴
- 플밍_김민장님
- GPGStudy
- 플밍_미친감자님
- 플밍_jz
- 플밍_샤방샤방님
- 플밍_글쓰는프로그래머2
- 플밍_키보드후킹
- 사람_재혁
- 사람_kernel0
- 사람_박PD
- 사람_경석형
- 사람_nemo
- 사람_kikiwaka
- 사람_Junios
- 사람_harry
- 사람_어떤 개발자의 금서목록..
- 사람_모기소리
- 사람_낙타한마리
- 사람_redkuma
- 사람_영원의끝
- 사람_민식형
- 도스박스 다음카페
- 플레이웨어즈 - 게임하드웨어벤치마크
- http://puwazaza.com/
- David harvey의 Reading Marx's c…
- 씨네21
- 한겨레_임경선의 이기적인 상담실
- 본격2차대전만화 - 굽시니스트
- 영화_정성일 글모음 페이지
- 영화_영화속이데올로기파악하기
- 음식_생선회
- 죽력고
- 사람_한밀
- 플밍_수까락
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 일리아스
- 태그가 아깝다
- 인문학
- 고전강의
- 소비자고발
- 건강
- 프로그래밍
- 정성일
- 유머
- programming challenges
- 진삼국무쌍5
- 정신분석
- 삼국지
- BSP
- 고등학교 사회공부
- 김두식
- modernc++
- Programming
- 영화
- 게임
- stl
- 강유원
- 노무현
- 삼국지6
- 단상
- template
- 진중권
- 책
- 유시민
- c++
Archives
- Today
- Total
01-07 02:28
lancelot.com
About C++ template 본문
- template instantiation 결과로 생성된 코드를 확인하고 싶을때
- compiler explorer site 에서 어셈블리의 생성을 확인 ( http://godbolt.org)
- https://cppinsights.io/ 에서 코드 생성을 확인 ( template 뿐만아니라 range for 등의 코드 생성도 볼 수 있음)
- 인스턴스화 된 함수 이름 출력 ( 비표준 MS 용 매크로 __FUNCSIG__, g++ 에서는 __PRETTYO_FUNCTION__ )
- C++20 의 <source_location> -> 하지만 MSVC에서는 __FUNCTION__ 처럼 동작해서 의미가 없음
- trailing return type
- template 에서 return type을 타입추론을 통해서 생성해야할 경우가 있는데, 함수 선언에서
decltype<a+b> Max(T a, T b)
처럼, 리턴타입 앞에 타입 추론 부분이 있으면 a, b 가 선언하기도전에 사용된거라서 오류가 발생.
그래서
auto max(T a, T b) -> decltype(a+b)
라고 뒤에다가 쓴다... 라고 하는데(C++11 에서 도입. 14부터는 앞에 쓸 수 있게 됨).. 좀 그렇다. 저걸 위해서 뒤에다가 너저분하게 쓰게 만들다니..... 진작에 앞에 쓸 수 있게 해주지 왜.... -_-;
- template parameter 로 들어갈 수 있는 것들의 종류 3가지 - type, tempate, non-type
그중에 non-type의 종류 - NTTP (Non-Type Template Parameter)
정수형 상수 | 컴파일시간 상수만 가능, 변수안됨 |
실수형 상수 | C++20 부터 지원 |
enum 상수 | enum 또는 enum class |
포인터, 함수포인터 | static storage 만 가능. 지역변수 주소 안됨 |
auto | C++ 17부터 가능 |
- template parameter의 auto(C++17) 와 function template의 auto(C++20) 를 구분할 것
// template parameter의 auto ( C++ 17 )
template<int N, double D, auto A> // auto는 int, double, pointer 등 모든 non-type parameter 전달 가능
struct Triple
{
};
// function template 의 auto ( C++ 20 )
void foo(auto a)
{
}
// 위의 것은 아래와 같다
template<typename T>
void foo(T a)
{
}
- array
#include<iostream>
#include<vector>
template<typename T, int N>
struct array
{
T arr[N] = {};
public :
std::size_t size() const { return N; }
T& operator[](int idx) { return arr[idx]; }
const T& operator[](int idx) const { return arr[idx]; }
using value_type=T;
using iterator = T*;
auto begin() { return arr; }
auto end() { return arr + N; }
};
int main()
{
int x[5] = { 1,2,3,4,5 };
std::vector<int> v = { 1,2,3,4,5 };
array<int, 5> a = {1,2,3,4,5};
std::cout << a.size() << std::endl;
a[0] = 10;
for (auto e : a)
std::cout << e<< std::endl;
}
- Template type deduction rule ( auto 의 타입이 결정되는 rule 도 같다)
// template type deduction rule
// T : 함수 인자가 가진 "const, volatile, reference 속성을 제거"하고 T타입을 결정
// T& : 함수 인자가 가진 "reference 속성만 제거"하고 T 타입을 결정. const, volatile 은 유지
// T&& : forwarding reference -> lvalue 와 rvalue를 모두 받을 수 있는 템플릿
// T T&&
// 3(rvalue) int int&&
// n(lvalue) int& int&
#include<iostream>
template<typename T>
void f3(T&& arg)
{
std::cout << __FUNCSIG__ << std::endl;
}
int main()
{
int n = 10;
int& r = n;
const int c = 10;
const int& cr = c;
f3(3);
f3(n);
f3(c);
f3(r);
f3(cr);
}
- arguement decay
#include<iostream>
template<typename T>
void f1(T arg)
{
std::cout << __FUNCSIG__ << std::endl;
}
template<typename T>
void f2(T& arg)
{
std::cout << __FUNCSIG__ << std::endl;
}
int main()
{
int x[3] = { 1,2,3, };
f1(x);// T=int* , arg=int*
f2(x);// T=int[3], arg=int(&)[3]
// int x[3] 일때 -> x의 정확한 타입은 int[3]
// auto a1=x; int a1[3]=x; -> compile error
// int* a1=x; -> compile ok
auto a1 = x; // auto = int* , int* a1=x;
auto& a2 = x;// auto = int[3]
// a2 = int(&)[3]
// argument decay : 배열 전달시 포인터로 받게되는 현상
// f1(T arg) T=int*
// f1(int* arg)
// f2(T&arg) T=int[3], arg=int(&)[3]
// f2(int(&arg)[3]
}
#include<iostream>
template<typename T>
void f1(T s1, T s2)
{
std::cout << __FUNCSIG__ << std::endl;
}
template<typename T>
void f2(T& s1, T& s2)
{
std::cout << __FUNCSIG__ << std::endl;
}
int main()
{
// 문자열의 정확한 타입 -> char 배열 ( char* 가 아님)
// "banana" -> char[7], "apple" -> char[6]
f1("banana", "apple"); // argument decay 때문에 const char* 로 변경되어서 compile ok
f2("banana", "apple"); // T 타입 1개를 받는데 입력은 char[7], char[6] 2가지라서 compile error
}
- explicit instantiation - template 은 선언과 정의를 같은 파일에 두어야하는데, 정의를 다른 파일에 두고 explicit instantiation 을 하면 compile 이 가능하다.
template<typename T>
void fn(T a)
{
}
template void fn<int>(int);// funciton template 의 explicit instantiation
template void fn<>(double);
template void fn(char);
template<class T>
class Type
{
void mf1(){}
void mf2(){}
};
template class Type<int>; // class template 의 explicit instantiation
template void Type<double>::mf1(); // class member function 의 explicit instantiation
int main()
{
// fn(3);
}
- specialization
#include<iostream>
template<typename T> // primary template
class Vector
{
T* ptr;
std::size_t size;
public :
Vector(std::size_t sz) : size(sz)
{
ptr = new T[sz];
}
~Vector() { delete[] ptr; }
};
template<> // template specialization
class Vector<bool>
{
int* ptr;
std::size_t size;
public:
Vector(std::size_t sz) : size(sz)
{
ptr = new int[sz/32+1];
}
~Vector() { delete[] ptr; }
};
template<typename T> // partial specialization
class Vector<T*>
{
T* ptr;
std::size_t size;
public:
Vector(std::size_t sz) : size(sz)
{
ptr = new T[sz];
}
~Vector() { delete[] ptr; }
};
int main()
{
Vector<int> v1(5);
Vector<double> v2(5);
Vector<bool> v3(5);
}
- primary template, specialization, partial specialization
#include<iostream>
// primary template
template<typename T, typename U>
struct Object
{
static void fn() { std::cout << "T, U" << std::endl;}
};
// specialization
template<>
struct Object<int, short>
{
static void fn() { std::cout << "int, short" << std::endl; }
};
// partial specialization
template<typename T, typename U>
struct Object<T*, U>
{
static void fn() { std::cout << "T*, U" << std::endl; }
};
template<typename T>
struct Object<T, T>
{
static void fn() { std::cout << "T, T" << std::endl; }
};
template<typename U>
struct Object<int, U>
{
static void fn() { std::cout << "int, U" << std::endl; }
};
template<typename A, typename B, typename C>
struct Object<A, Object<B, C>>
{
static void fn() { std::cout << "Object<A, Object<B, C>>" << std::endl; }
};
int main()
{
Object<char, double>::fn(); // T, U
Object<int, short>::fn(); // int, short
Object<short*, double>::fn(); // T*, U
Object<float, float>::fn(); // T, T
Object<int, float>::fn(); // int, U
Object<long, Object<char, short>>::fn(); // A, Object<B, C>
}
- template partial specialization 시의 default parameter
#include<iostream>
template<typename T1, typename T2=T1>
struct Object
{
static void print()
{
std::cout << typeid(T1).name() << ", " << typeid(T2).name() << std::endl;
}
};
// partial specialization 에는 default parameter 를 표기하지 않고, primary template 의 것을 가져와서 사용함
template<typename T1, typename T2>
struct Object<T1*, T2>
{
static void print()
{
std::cout << typeid(T1).name() << ", " << typeid(T2).name() << std::endl;
}
};
int main()
{
Object<int*>::print(); // 이경우 int, int* 로 출력
}
- specialization 또는 partial specialization 버전만 사용하고 싶을때는 primary template을 선언만 제공한다
// 특수화 또는 부분 특수화 버전만 사용하고, primary template 은 사용하지 않기를 원할때는
// primary template을 선언만 제공한다.
template<typename T, int N>
struct Object;
template<typename T>
struct Object<T, 1>
{
static void print() { std::cout << typeid(T).name() << ", " << std::endl; }
};
template<typename T>
struct Object<T, 2>
{
static void print() { std::cout << typeid(T).name() << ", " << std::endl; }
};
int main()
{
Object<int, 1>::print();
Object<int, 2>::print();
Object<int, 3>::print();// error
}
- variable template specialization
#include<iostream>
// variable template 의 specialization 을 이용하면 편리한 경우가 있다.
// ex> class의 버전 제공
template<typename>
inline constexpr int version = -1;
class AAA;
template<>
inline constexpr int version<AAA> = 2020;
class BBB;
template<>
inline constexpr int version<BBB> = 2021;
int main()
{
std::cout << version<AAA> << std::endl;
std::cout << version<BBB> << std::endl;
std::cout << version<int> << std::endl; // version 정보를 제공하지 않을 경우 default -1
}
- borrowed range
- C++ 20 에 소개된 용어
- 자원을 소유하지 않고 다른 range(container)가 소유한 자원을 사용하는 range
- std::ranges::enable_borrowed_range 라는 variable template으로 조사가능
- <ranges>
- variable template specialization 으로 구현
#include<iostream>
// C++ 20 의 enable_borrowed_range, <ranges>
template<typename>
inline constexpr bool enable_borrowed_range = false;
template<typename T, typename Traits>
inline constexpr bool enable_borrowed_range<std::basic_string_view<T, Traits> > = true;
int main()
{
std::string s1 = "to be or not to be";
std::string s2 = s1;
std::string_view sv = s1;
std::cout << enable_borrowed_range<std::string> << std::endl; // 0
std::cout << enable_borrowed_range<std::string_view> << std::endl; // 1
}
- integral constant
- int2type - 0과 1은 같은 type이라서 function overloading을 이용할 수 없지만, int2type<> 템플릿을 사용해서 템플릿에 넣으면 다른 타입으로 만들어서 함수 오버로딩을 이용할 수 있게됨.
- C++11 에서 integral constant로 발전
#include<iostream>
template<int N>
struct int2type{ enum { value = N }; };
void fn(int n) { std::cout << __FUNCSIG__ << std::endl; }
void fn(int2type<0>) { std::cout << __FUNCSIG__ << std::endl; }
void fn(int2type<1>) { std::cout << __FUNCSIG__ << std::endl; }
int main()
{
fn(0);
fn(1);
int2type<0> t1;
int2type<1> t2;
fn(t1);
fn(t2);
}
- 이것을 이용하면, 타입에 따라 빌드되지 않는 템플릿 코드를 만들 수 있게됨.
#include<iostream>
template<typename T>
struct is_pointer { enum { value=false }; };
template<typename T>
struct is_pointer<T*> { enum { value = true }; };
template<int N>
struct int2type { enum { value=N }; };
template<typename T>
void printv_imp(const T& value, int2type<1>)
{
std::cout << value << " : " << *value << std::endl;
}
template<typename T>
void printv_imp(const T& value, int2type<0>)
{
std::cout << value << std::endl;
}
template<typename T>
void printv(const T& value)
{
printv_imp(value, int2type<is_pointer<T>::value>());
}
int main()
{
int n = 10;
printv(&n);
printv(n);
}
// int2type
// -> int 타입의 상수를 type으로 만드는 도구
// -> int 뿐만 아니라 다른 정수형 타입의 상수도 타입으로 만들수 있으면 좋지않을까?
// std::integral_constant
// -> C++11 표준
// -> int2type의 발전된 형태
template<int N> struct int2type
{
enum { value=N };
};
template<typename T, T N>
struct integral_constant
{
static constexpr T value= N;
};
int main()
{
int2type<0> t1;
int2type<1> t2;
integral_constant<int, 0> n0;
integral_constant<int, 1> n1;
integral_constant<short, 1> n2;
}
- type transformation
- T가 포인터인지 조사하는 방법
- C++11 std::is_pointer<T>::value
- C++17 std::is_pointer_v<T>
- T가 포인터인지 조사하는 방법
// variable template을 이용
template<typename T>
constexpr bool is_pointer_v=std::is_pointer<T>::value;
- T에서 포인터를 제거한 타입 구하기
- C++11 typename std::remove_pointer<T>::value
- C++14 std::remove_pointer_t<T>
// using template 을 이용
template<typename T>
using remove_pointer_t=std::remove_pointer<T>::type;