Effective Modern C++ Item 2 내용 정리
Chap 1. 형식 연역 (Type deduction)
Item 2. auto 형식 연역 규칙 (auto type deduction)
📌 Main Point
auto
의 형식 연역은 대체로 템플릿 형식 연역과 같지만, auto 형식 연역은 중괄호 초기치가 std::initializer_list
를 나타낸다고 가정하는 반면 템플릿 형식 연역은 그렇지 않다는 차이가 있음
- 함수의 반환 형식이나 람다 매개변수에 쓰인
auto
에 대해서는 auto 형식 연역이 아니라 템플릿 형식 연역이 적용됨
💡 템플릿 형식 연역과 동일한 auto의 형식 연역
- Item 1에서 템플릿 형식 연역을 배웠다면 auto 형식 연역의 거의 전부를 이미 알고 있는 것임 (한가지 예외 사항을 빼면 auto 형식 연역과 템플릿 형식 연역이 동일하기 때문)
- 템플릿 형식 연역과 auto 형식 연역 사이에는 직접적인 대응 관계가 존재하고, 둘을 알고리즘적으로 상호 변환할 수 있음
📌 Main Point
auto
의 형식 연역은 대체로 템플릿 형식 연역과 같지만, auto 형식 연역은 중괄호 초기치가std::initializer_list
를 나타낸다고 가정하는 반면 템플릿 형식 연역은 그렇지 않다는 차이가 있음- 함수의 반환 형식이나 람다 매개변수에 쓰인
auto
에 대해서는 auto 형식 연역이 아니라 템플릿 형식 연역이 적용됨
✍ Ex) Item 1에서 다룬 템플릿 형식 연역과 auto 형식 연역의 관계
template<typename T>
void f(ParamType param);
f(expr); // 어떤 표현식으로 f를 호출
👉 f
호출에서 컴파일러는 expr
을 이용해서 T
의 형식과 ParamType
의 형식을 연역함
🔑 auto
를 이용해서 변수를 선언할 때 auto
는 template
의 T
와 동일한 역할을 하며, 변수의 형식 지정자(type specifier)는 ParamType
과 동일한 역할을 함
✍ Ex) auto
의 형식 연역에 대응되는 템플릿 형식 연역
auto x = 27; // x의 형식 지정자는 auto
const auto cx = x; // cx의 형식 지정자는 const auto
const auto& rx = x; // rx의 형식 지정자는 const auto&
👉 위의 코드에서 x
와 cx
, rx
의 형식들을 연역할 때, 컴파일러는 마치 선언마다 템플릿 함수 하나와 해당 초기화 표현식으로 그 템플릿 함수를 호출하는 구문이 존재하는 것처럼 (= 아래의 코드처럼) 행동함
template<typename T>
void func_for_x(T param); // x의 형식 연역을 위한 개념적인 템플릿
func_for_x(27); // 개념적인 호출: param에 연역된 형식 = x의 형식
template<typename T>
void func_for_cx(const T param); // cx의 형식 연역을 위한 개념적인 템플릿
func_for_cx(x); // 개념적인 호출: param에 연역된 형식 = cx의 형식
template<typename T>
void func_for_rx(const T& param); // rx의 형식 연역을 위한 개념적인 템플릿
func_for_rx(x); // 개념적인 호출: param에 연역된 형식 = rx의 형식
✍ Ex) auto
의 형식 연역 (case 1-3)
auto x = 27; // Case 3: x는 포인터도, 참조도 아님
const auto cx = x; // Case 3: cx는 포인터도, 참조도 아님
const auto& rx = x; // Case 1: rx는 보편 참조가 아닌 참조
auto&& uref1 = x; // Case 2: x는 int이자 왼값
// → uref1의 형식은 int&
auto&& uref2 = cx; // Case 2: cx는 const int이자 왼값
// → uref2의 형식은 const int&
auto&& uref3 = 27; // Case 2: 27는 int이자 오른값
// → uref3의 형식은 int&&
🔑 Item 1에서 템플릿 형식 연역을 일반적인 함수 템플릿의 param
의 형식 지정자인 ParamType
의 특성에 따라 세가지 경우로 나누어서 다뤘음
🔑 auto
를 이용한 변수 선언에서는 변수의 형식 지정자가 ParamType
의 역할을 하므로, auto 형식 연역 역시 세 가지 경우로 나뉨
✔ Case 1: 형식 지정자가 포인터 또는 참조 형식이지만 보편 참조(universal reference)는 아닌 경우
✔ Case 2: 형식 지정자가 보편 참조인 경우
✔ Case 3: 형식 지정자가 포인터도, 참조도 아닌 경우
✍ Ex) auto의 형식 연역 (배열, 함수)
const char name[] = "R. N. Briggs"; // name의 형식은 const char[13]
auto arr1 = name; // arr1의 형식은 const char*
auto& arr2 = name; // arr2의 형식은 const char(&)[13]
void someFunc(int, double); // someFunc의 형식은 void(int, double)
auto func1 = someFunc; // func1의 형식은 void (*)(int, double)
auto& func2 = someFunc; // func2의 형식은 void (&)(int, double)
🔑 Item 1에서 비참조 형식 지정자의 경우 배열과 함수 이름이 포인터로 붕괴하는 방식을 다뤘는데, auto 형식 연역에서도 동일하게 적용됨
💡 auto의 형식 연역 차이점 (vs 템플릿 형식 연역)
- 위의 예시와 같이 auto의 형식 연역은 템플릿 형식 연역과 똑같이 작동하지만 다른 점이 하나 있음
✍ Ex) 27을 초기 값으로 int 변수를 선언 (auto의 형식 연역 차이점)
🔑 C++98에서는 아래 두 가지 구문이 가능했음
int x1 = 27;
int x2(x1);
🔑 균일 초기화(uniform initialization)를 지원하는 C++11에서는 아래의 구문을 추가로 사용할 수 있음
int x3 = { 27 };
int x4{ 27 };
👉 총 네 가지 구문이 존재하지만, 값이 27
인 int
가 생긴다는 점은 모두 동일함
👉 고정된 형식 대신 auto
를 이용해서 변수를 선언하는 데에는 몇 가지 장점이 있어서, 위의 구문을 auto
로 치환하게 되면 아래와 같은 선언이 됨
auto x1 = 27; // 형식은 int, 값은 27
auto x2(x1); // 형식은 int, 값은 27
auto x3 = { 27 }; // 형식은 std::initializer_list<int>, 값은 { 27 }
auto x4{ 27 }; // 형식은 std::initializer_list<int>, 값은 { 27 }
👉 위의 선언들은 모두 문제 없이 컴파일 되지만, 이전과는 의미가 달라진 구문이 생김 (아래 두 구문)
⛔ auto
로 선언된 변수의 초기치(initializer)가 중괄호 쌍으로 감싸진 형태이면, 연역된 형식은 std::initializer_list
임
⛔ 만약 그런 형식(= std::initializer_list
)을 연역할 수 없으면 (e.g. 중괄호 초기치의 값들의 형식이 서로 다른 경우) 컴파일이 거부됨
auto x5 = { 1, 2, 3.0 }; // 오류! std::initializer_list<T>의 T를 연역할 수 없음
🔑 auto
형식 연역과 템플릿 형식 연역은 중괄호 초기치가 관여할 때에만 차이를 보임
🔑 auto
로 선언된 변수를 중괄호 초기치로 초기화하는 경우, 연역된 형식은 std::initializer_list
의 한 인스턴스
⛔ 해당 템플릿 함수에 동일한 중괄호 초기치를 전달하면 형식 연역이 실패해서 컴파일이 거부됨
auto x = { 11, 23, 9 }; // x의 형식은 std::initializer_list<int>
template<typename T> // x의 선언에 해당하는 매개변수 선언을 가진 템플릿
void f(T param);
f({ 11, 23, 9 }); // 오류! T에 대한 형식을 연역할 수 없음
🔑 param
의 형식이 어떤 알려지지 않은 T
에 대한 std::initializer_list<T>
인 템플릿에 그 중괄호 초기치를 전달하면 템플릿 형식 연역 규칙들에 의해 T
의 형식이 제대로 연역됨
template<typename T>
void f(std::initializer_list<T> initList);
f({ 11, 23, 9 }); // T는 int로 연역되며,
// initList의 형식은 std::initializer_list<int>로 연역됨
✔ auto 형식 연역과 템플릿 형식 연역의 실질적인 차이
⛔ auto는 중괄호 초기치가 std::initializer_list
를 나타낸다고 가정하지만, 템플릿 형식 연역은 그렇지 않음
⛔ auto
를 이용해서 변수를 선언하면서 그 변수를 중괄호 초기치로 초기화할 때에는 변수의 형식이 std::initializer_list
로 연역됨
✔ C++11 프로그래밍에서 저지르는 전형적인 실수 중 하나
⛔ 변수를 선언할 때 그 변수가 원래 의도한 형식이 아닌 std::initializer_list
로 선언되게 만드는 것
✔ C++14에서의 추가 사항
✍ Ex) 중괄호 초기치를 돌려주는 함수의 반환 형식을 auto로 지정하면 컴파일 실패
🔑 C++14에서는 함수의 반환 형식을 auto
로 지정해서 컴파일러가 연역하게 만들 수 있으며, 람다의 매개변수 선언에 auto
를 사용하는 것도 가능
⛔ 하지만 auto
의 그러한 용법들에는 auto 형식 연역이 아니라 템플릿 형식 연역 규칙들이 적용됨 → 중괄호 초기치를 돌려주는 함수의 반환 형식을 auto
로 지정하면 컴파일이 실패
auto createInitList()
{
return { 1, 2, 3 }; // 오류! { 1, 2, 3 }의 형식을 연역할 수 없음
}
⛔ C++14 람다의 매개변수 형식 명세에 auto
를 사용하는 경우에도 마찬가지 이유로 컴파일이 실패
std::vector<int> v;
…
auto resetV =
[&v](const auto& newValue) { v = newValue; }; // C++14
…
resetV({ 1, 2, 3 }); // 오류! { 1, 2, 3 }의 형식을 연역할 수 없음
Author And Source
이 문제에 관하여(Effective Modern C++ Item 2 내용 정리), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@minsukim/EMCPP-Item02저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)