Item 5: Prefer auto to explicit type declarations
auto를 사용하면 어떤 장점이 있는가?
Auto variables must be initialized : Avoiding uninitialized variable problems
auto variables는 initializer로부터 타입을 추론하기 때문에, 항상 초기화되어야 한다.
따라서, 초기화되지 않은 변수 문제로부터 벗어날 수 있다.
Represent types knwon only to compilers
auto는 type deduction을 사용하기 때문에, 컴파일러에만 있는 타입을 지정할 수 있다.
Auto vs std::function
std::function은 C++11 표준 라이브러리의 템플릿으로, 함수 포인터 개념을 일반화한다.
함수뿐만 아니라 호출 가능한 어떠한 객체도 가리킬 수 있다.
auto derefUPLess = [](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2){ return *p1<*p2; };
std::function<bool(const std::unique_ptr<Widget>&, const std::unique_ptr<widget>&)>
std::function객체는 전형적으로 auto-declared object보다 더 많은 메모리를 사용한다.
그리고 inlining 제한과 간접적인 함수 호출을 야기하는 구현 디테일 때문에, std::function 객체를 통한 closure 호출은 auto-declared 객체를 통한 호출보다 느리다.
다시말해서, std::function 접근은 일반적으로 auto 접근보다 더 크고 느리다.
이것은 out-of-memory 예외를 발생시킬 수 있다.
auto를 사용하면, 직접적으로 closures를 hold할 수 있다.
Type shortcuts
auto를 사용하면 너가 선언한 변수의 타입과 초기화를 위한 표현식의 타입간의 불일치 문제를 걱정하지 않아도 된다.
Things to Remember
auto 변수는 초기화되어야 하며, 일반적으로 portability(이식성)또는 efficiency(효율성) 문제로 이어질 수 있는 타입 불일치에 영향을 받지 않으며, refactoring process를 용이하게 할 수 있으며, 일반적으로 명시적으로 지정된 타입이 있는 변수보다 입력이 덜 필요하다.
auto-typed variable들은 item2와 6에 설명된 함정들이 있다.
Item 6: Use the explicitly typed initializer idiom when auto deduces undesired types
다음과 같은 코드를 살펴보자.
std::vector<bool> features(const Widget& w);
Widget w;
bool highPriority = features(w)[5];
processWidget(w, highPriority);
std::vector<bool>은 효율을 위해 1 bit 단위의 압축된 형태로 저장된다.
std::vector은 개념적으로 bool을 가지지만, std::vector의 operator[]는 container의 element에 대한 reference를 리턴하지 않고, 대신에 std::vector::reference 타입의 객체를 리턴한다.
std::vector<bool> 객체는 bool&처럼 동작하는 객체를 리턴한다.
이 작업이 성공하려면 std::vector<bool>::reference 객체를 bool&가 할 수 있는 모든 context에서 기본적으로 사용할 수 있어야 한다.
std::vector<bool>::reference의 기능 중에는 bool로의 암시적 변환이 있다.
highPriority를 초기화 하기위해 bool로 암시적 형 변환이 일어난다.
highPriority의 타입이 auto라면, 어떤 일이 발생하는가?
auto highPriority = features(w)[5];
features는 std::vector<bool> 객체를 반환하고, operator[]가 호출되며, operator[]는 std::vector<bool>::reference 객체를 반환한다.
여기서 다른 점은, auto가 highPriority의 타입을 추론한다는 것이다.
highPriority는 features에 의해 리턴되는 std::vector<bool>의 bit 5의 값을 가지지 않는다.
features에 대한 호출은 일시적인 std::vector<bool>객체를 반환한다.
operator[]는 std::vector<bool>::reference를 반환하는데, 이 객체는 일시적인 객체에 의해 관리되는 bits들의 포인터와 5 bit의 오프셋을 포함한다.
highpriority도 이 std::vector<bool>::reference를 가리킨다.
그러나 이 구문의 마지막에서, 일시적인 객체가 소멸되고, highPriority는 dangling(허상의) pointer를 가리킨다.
이것은 processWidget 함수 호출에서의 정의되지 않은 행동을 유발한다.
Proxy Class
std::vector<bool>::reference는 proxy class의 예시다.
이 클래스는 다른 타입의 동작을 emulate하거나 보강할 목적으로 존재한다.
Proxy class의 예로 std::vector<bool>::reference, smart pointer가 있다.
std::vector<bool>::reference는 std::vector<bool>의 operator[]가 bit에 대한 reference를 반환하는 환상을 제공하고, smart pointer는 raw pointer에 자원 관리를 더한 기능을 제공한다.
Proxy class중에 std::shared_ptr, std::unique_ptr처럼 클라이언트에게 명확하게 보이도록 디자인되는 클래스도 있지만, 더 보이거나 덜 보이게(invisible) 설계된 클래스도 있다.
std::vector<bool>::reference는 "invisible" proxies의 예시다.
"Invisible" proxy types problem
invisible proxy classes는 auto와 잘 동작하지 않는다.
invisible proxy class는 종종 한 statement에서만 살아있도록 설계되었다.
따라서 이러한 타입의 변수를 만드는 것은 기본적인 클래스 설계에 위반되는 것이다.
이것을 어기는 경우 정의되지 않은 행동을 할 수 있다.
따라서 다음과 같은 형태의 코드를 피해야 한다.
auto someVar = expression of "invisible" proxy class type;
How to recognize when proxy objects are in use
대부분의 라이브러리는 invisible proxy object을 사용하는 경우 문서에 남겨 놓는다.
문서가 짧다면, 소스코드에서 정보를 얻을 수도 있다.
일반적으로 클라이언트가 호출할 것으로 예상되는 함수에서 리턴되기 때문에, 함수 signatures는 그 존재를 반영한다.
클래스의 인터페이스에 집중하면 자주 proxy class들의 존재를 알아낼 수 있다.
Explicitly typed initializer idiom
명시적인 타입 초기화자를 사용하여 다른 타입으로 추론을 강제함으로써 invisible proxy class에서 auto를 사용할 수 있다.
auto를 사용하여 추론할 타입으로 초기화 식을 casting한다.
auto highPriority = static_cast<bool>(features(w)[5]);
Things to Remember
"invisible" proxy types는 auto가 초기화 표현식에 대해 잘못된 타입을 추론하도록 할 수 있다.
The explicitly typed initializer idiom은 auto가 원하는 타입을 추론하도록 강제한다.
'C++ > Effective Modern C++' 카테고리의 다른 글
[Effective Modern C++] Chapter 4: Smart Pointers (0) | 2022.07.26 |
---|---|
[Effective Modern C++] Chapter 3: Moving to Modern C++(1) (0) | 2022.07.20 |
[Effective Modern C++] Chapter 1: Deducing Types (1) | 2022.07.18 |