Effective Modern C++ - Scott Meyers
item 1. Understand template type deduction
item 1. template 타입 추론을 알아보자.
* Intro
C++98 에는 타입추론 (type deduction) 규칙이 하나 밖에 없었다. function template에서만 사용되었다. 하지만 거기에 대해서 자세히 알지 못해도, 사는데 큰 지장이 없었다. 하지만 C++ 11 이후에 type 추론이 2가지가 더 생겼다. auto 와 decltype에서 type 추론을 사용한다.
여기에 Good news 와 Bad news가 있다.
- Good news는 auto의 type deduction은 C++98의 template type deduction을 바탕으로 하고 있다는 점이다.
- Bad news는 그동안 몰라도 행복할수 있었던 template type deduction에 대해서 이제는 이해할 필요가 생겼다.
먼저 앞으로 설명할 내용에 대해서 용어를 정의해보자.
template<typename T> // T 는 template에서의 type을 의미
void f(ParamType param); // ParamType은 T로 표현되는 함수정의에 적힌 param의 type
f(expr); // expr은 함수 호출시 사용되는 표현
|
위 예제에서 Compiler는 T와 ParamType의 type deduction을 위해 expr를 사용한다.
그중 T의 type은 expr뿐만 아니라 ParamType의 영향도 받는다.
* 3 Cases for template type deduction
template의 type deduction 방법에는 3가지 경우에 대해서 생각해 보면 된다.
Case 1 : ParamType이 Reference (&) 이거나 Pointer (*) 인 경우. 단 Universal Reference (&&)는 제외
1. expr이 reference (&)면, expr의 reference (&)를 무시
2. 그런 다음 expr의 type과 ParamType을 pattern-match하여 T의 type을 판단한다.
위 2가지 규칙만 적용하면 쉽긴한데, 예제를 몇개 보면 좀 더 명확해 질 것이다.
먼저 ParamType이 T & 인 경우에 대해서 한 번 보자.
template <typename T>
f(T & param); // ParamType : T &
int x = 27; // expr : int
const int cx = x; // expr : const int
const int & rx = x; // expr : const int&
f(x); // T : int , ParamType : int &
f(cx); // T : const int, ParamType : const int&
f(rx); // T : const int, ParamType : const int&
|
아래 예제는 ParamType이 T & 인 경우에 대해서 한 번 보자.
template <typename T>
f(const T& param); // ParamType : const T &
int x = 27; // expr : int
const int cx = x; // expr : const int
const int & rx = x; // expr : const int&
f(x); // T : int, ParamType : const int&
f(cx); // T : int, ParamType : const int&
f(rx); // T : int, ParamType : const int&
|
다음으로는 Pointer (*)의 경우에 대해서 한번 보자.
template <typename T>
f(T * param); // ParamType : T *
int x = 27; // expr : int -> int*
const int* px = &x; // expr : const int*
f(&x); // T : int , ParamType : int*
f(px); // T : const int, ParamType : const int*
|
Case 2 : ParamType이 Universal Reference (&&)인 경우
1. expr이 lvalue 인 경우, T와 ParamType을 모두 lvalue reference (&)로 추론하면 된다.
(쉽게 말해서 ParamType의 &를 하나 빼고, expr은 전부다 &를 붙여주고 그냥 Case 1 규칙 적용하면 끝)
2. expr이 rvalue 인 경우, 그냥 Case 1과 같이 적용하면 된다.
template <typename T>
f(T&& param); // ParamType : T&& (Universal reference)
int x = 27;
const int cx = x;
const int & rx = x;
f(x); // lvalue => T : int & , ParamType : int&
f(cx); // lvalue => T : const int & , ParamType : const int&
f(rx); // lvalue => T : const int & , ParamType : const int&
f(27); // rvalue => T : int , ParamType : int&&
|
Case 3 : ParamType이 그냥 Call-by-Value인 경우
- reference (&), volatile, const 모두 다 무시한다.
어차피 expr의 값을 복사해서 사용하는 것이니 원래 전달되기 전의 값이 어떤 특성을 가졌는지는 아무런 의미가 없다. 그냥 복사해서 함수안에서 구워먹든 삶아먹든 다 허용된다.
단, 문자열을 저장한 const char* const str 같은걸 전달할때는 조금 생각을 해봐야 하는데, pointer 그 자체의 const만 무시한다. 즉, 문자열값 자체는 바꿀수가 없지만, 해당 pointer가 다른것을 가리키도록 변경 할 수는 있다.
template <typename T>
f(T param);
const char* const str = "Luna the Star";
|
C++에서 배열값을 인자로 넘길때는 pointer로 처리된다.
ParamType이 T인 경우 expr로 배열을 넘기면 pointer로 넘어가게 되는데,
ParamType이 T &인 경우는 pointer가 아닌 배열로 추론된다.
template <typename T>
f(T param); // ParamType : T
template <typename T>
fr(T & param); // ParamType : T&
const char str[] = "Luna the Star";
const char* pStr = str;
f(str); // 둘 다 T : const char*
f(pStr);
fr(str); // T : const char (&)[]
fr(pStr); // T : const char* |
template<typename T, std::size_t N>
constexpr std::size_t arraySize(T(&)[N]) noexcept
{
return N;
}
int keyValues[] = { 1,2,3,4,5,6,7 };
std::cout << arraySize(keyValues) << std::endl;
|
Function Arguments
function pointer의 경우 ParamType이 T 이면 param이 pointer (*)로 처리되고, T & 이면 param이 reference (&)로 처리된다.
void someFunc(int, double); //void(int, double)
template<typename T>
void f1(T param);
template<typename T>
void f2(T& param);
f1(someFunc); // ptr to func : void (*)(int, double)
f2(someFunc); // ref to func : void (&)(int, double)
|
Things to Remember * template type deduction시 reference (&)인 인자의 &는 무시된다. * Universal reference (&&) 인자를 type deduction 할때 lvalue인 경우는 특별하게 처리한다. (T, ParamType 둘다 &가 있는 걸로 처리) * call-by-value 인자의 type deduction 할때는 const, volatile등의 키워드는 모두 무시된다. * 배열, 함수가 인자로 type deduction 할때는 decay to pointer 규칙에 의해 pointer로 처리된다. 단 ParamType에 &가 붙은 경우는 원래 type 그대로 참조된다. |
댓글 없음:
댓글 쓰기