codenuri 강석민 강사 강의 내용기반으로 정리한 내용입니다.
boost::type_index
- C++ 표준의 typeid() 연산자 사용
- const, volatile, reference 를 조사할 수 없음
- boost의 type_index 라이브러리 사용
- const, volatile, reference 를 조사 할 수 있음
#include <iostream>
using namespace std;
template<typename T> void foo(const T a)
{
cout << "T : " << typeid(T).name() << endl;
cout << "a : " << typeid(a).name() << endl;
}
int main()
{
// foo<int>(3);
foo(3); // T: int, a: const int
foo(3.3); // T: double, a: const double
}
출력결과는 모두 int, double 임 -> boost 사용
- <boost\type_index.hpp>
- namespace boost::typeindex 안에 있음
- type_id_with_cvr
().pretty_name() 으로 사용 - 변수의 타입을 조사 할 때는 decltype()을 사용함
- type_id_with_cvr<decltype(a)>().pretty_name()
#include <iostream>
#include <boost\type_index.hpp>
using namespace std;
using namespace boost::typeindex;
template<typename T> void foo(const T a)
{
cout << type_id_with_cvr<T>().pretty_name() << endl;
cout << type_id_with_cvr<decltype(A)>().pretty_name() << endl;
}
int main()
{
foo(3); // T: int, a: const int
foo(3.3); // T: double, a: const double
}
출력결과
Template type deduction
Template Argument Type Deduction
- 컴파일러가 함수 인자를 보고 템플릿의 타입을 결정하는 것
- 함수 인자의 타입과 완전히 동일한 타입으로 결정되지 않는다.
아래 모두 T는 int 로 결정됨
template<typename T> void foo(T a)
{
++a;
}
int main()
{
int n = 0;
int& r = n;
const int c = n;
foo(n); // T: int
foo(c); // T: const int ? int
foo(r); // T: int& ? int (원래 원본은 바뀌지 않는 것을 기대)
}
- 규칙1. 템플릿 인자가 값 타입일 때(T a)
- 함수 인자가 가진 const, volatile, reference 속성을제거하고 T의 타입을 결정
- 주의! - 인자가 가진 const 속성만 제거 된다.
// 함수 템플릿 인자가 값 타입(T a) 일 때.
template<typename T> void foo(T a)
{
cout << type_id_with_cvr<T>().pretty_name() << endl;
}
int main()
{
int n = 0;
int& r = n;
const int c = n;
const int& cr = c;
foo(n); // T: int
foo(c); // T: int
foo(r); // T: int
foo(cr); // T: int
const char* s1 = "hello"; // s1을 따라가면 const
foo(s1); // T: char const*
const char* const s2 = "hello";
foo(S2); // T: char const*
}
- 규칙2. 템플릿 인자가 참조 타입일 때(T& a)
- 함수 인자가 가진 reference 속성만 제거하고 T의 타입을 결정
- const 와 volatile 속성은 유지한다. 단, 템플릿 인자가 const T& 일 경우는 함수 인자가 가진 const를 제거하고 T 타입을 결정
// 함수 템플릿 인자가 참조 타입(T& a) 일 때.
template<typename T> void foo(T& a)
{
cout << type_id_with_cvr<T>().pretty_name() << endl;
cout << type_id_With_cvr<decltype(a)>().pretty_name() << endl;
}
// 함수 템플릿 인자가 const 참조 타입(const T& a) 일 때.
template<typename T> void goo(const T& a)
{
cout << type_id_with_cvr<T>().pretty_name() << endl;
cout << type_id_With_cvr<decltype(a)>().pretty_name() << endl;
}
int main()
{
int n = 0;
int& r = n;
const int c = n;
const int& cr = c;
foo(n); // T: int, a: int&
foo(c); // T: const int, a: const int& (const를 일반참조로 가르킬 수 없음->const 유지시킴)
foo(r); // T: int, a: int&
foo(cr); // T: const int, a: const int&
goo(n); // T: int, a: const int&
goo(c); // T: int, a: const int&
goo(r); // T: int, a: const int&
goo(cr); // T: int, a: const int&
}
정리
template<typename T> void foo(T a) {}
template<typename T> void foo(T& a) {}
template<typename T> void foo(const T& a) {}
template<typename T> void foo(T&& a) {}
int n = 10;
const int& cr = n;
foo(cr);
int x[3] = {1,2,3};
foo(x); // 배열을 함수 템플릿에 전달할 때 주의점
1.규칙1
2.규칙2
3.규칙3. 템플릿 인자가 forwarding 레퍼런스일때(T&&)
- lvalue와 rvalue를 모두 전달 받는다.
4.규칙4. 배열을 전달받을때 - argument decay 발생
array name
int main()
{
int n; // 변수 이름: n, 타입: int
int *p1 = &n;
double d; // 변수 이름: d, 타입: double
double *p2 = &d;
int x[3] = {1,2,3}; // 변수이름: x, 타입: int[3] (이름을 빼면 타입만 남음)
// 배열 x의 주소..
int (*p3)[3] = &x; // *과 [] 연산자 2개가 있어 우선순위 높이기 위해 괄호를 붙임
int *p4 = x; // 배열의 주소 아님..
}
int main()
{
int n1 = 10;
int n2 = n1; // 모든 변수는 자신과 동일한 타입의 변수로 초기화(복사) 될 수 있다.
double d1 = 3.4;
double d2 = d1;
int x1[3] = {1,2,3}; // x1의 타입 : int[3]
int x2[3] = x1; // error. 배열은 크기가 커 자산과 동일한 타입의 배열로 복사가 안됨...
int *p1 = x1; // 대신 배열의 이름은 첫번째 요소의 주소로 암시적 형변환 된다.
}
int main()
{
int x1[3] = {1,2,3};
int(*p1)[3] = &x; // 배열의 주소
int *p2 = x; // 배열의 이름이 배열의 첫번째 요소의 주소로 암시적 형변환 된 표현
printf("%p, %p\n", p1, p1+1); //12
printf("%p, %p\n", p2, p2+1); //4
}
int main()
{
int x1[3] = {1,2,3};
int(*p1)[3] = &x; // 배열의 주소
int *p2 = x; // 배열의 이름이 배열의 첫번째 요소의 주소로 암시적 형변환 된 표현
printf("%p, %p\n", p1, p1+1); //12
printf("%p, %p\n", p2, p2+1); //4
// p1: 배열의 주소, *p1: 배열
(*p1)[0] = 10;
// p2: 요소의 주소, (int*)
*p2 = 10;
}
배열의 이름이 배열의 첫번째 요소의 주소로 암시적 형변환 된다는 원리 이해하기!
argument decay
- 배열의 특징
- 자신과 동일한 타입으로 초기화 될 수 없다.
- 배열의 이름은 배열의 1번째 요소의 주소로 암시적 형 변환 된다.
- 배열을 가리키는 참조를 만들 수 있다.
- 함수 템플릿을 만들 때
- 배열을 값으로 받으면 T는 요소 타입의 포인터로 결정된다.
- 배열을 참조로 받으면 T는 배열 타입으로 결정된다.
template<typename T>
// void foo(T a) // void foo(int a[3])
void foo(T a) // void foo(int* a) -> argumnet Decay(배열을 포인터로 받음)
{
cout << type_id_with_cvr<T>().pretty_name() << endl;
cout << type_id_with_cvr<decltype(a)>().pretty_name() << endl;
}
template<typename T>
void goo(T& a) // void goo(int (&a)[3])
{
cout << type_id_with_cvr<T>().pretty_name() << endl;
cout << type_id_with_cvr<decltype(a)>().pretty_name() << endl;
}
int main()
{
int x1[3] = {1,2,3};
foo(x); // T: int[3] error
goo(x); // T: int[3] ok
int y[3] = x; // error
int *p = x; // ok
int (&r)[3] = x; // ok
}
출력결과
–
문자열의 타입: char 배열
문자열을 값으로 받으면 T는 const char* 결정되고, 참조로 받으면 const char[]로 결정된다.
크기가 다른 배열은 다른 타입임.
template<typename T> void foo(T a, T b)
{
}
template<typename T> void goo(T& a, T& b)
{
}
int main()
{
// foo("orange", "apple");
// goo("orange", "apple"); // error (크기가 다르므로 다른 타입)
goo("orange", "aapple");
}