Post List

2015년 3월 26일 목요일

item 05: 구체적 타입 선언보다 auto를 더 활용하자.

작가
Meyers, Scott
출판
O'ReillyMedia
발매
2014.12.12
평점








Effective Modern C++ - Scott Meyers
Item 5 : Prefer auto to explicit type declarations.

item 5 구체적 타입 선언보다 auto를 더 활용하자.

대부분 당연히  auto 보다는 구체적으로 타입을 선언하는게 더 좋을 것이라고 생각한다.
뭐든 사용하기 편하면 그만큼 성능적으로 Cost가 있을 것이라고 생각하기 때문이다.
하지만 꼭 그렇지 않은게 바로 auto이다.
auto를 사용하면 사용하기 편리할 뿐 아니라 성능상의 이익도 얻을 수 있다.

auto를 사용했을 때 어떤 점이 좋은지 한번 알아보자.

1. 무조건 변수를 초기화 해줘야 한다.

auto를 사용하면서 변수 초기화를 해주지 않으면 컴파일시 오류가 발생한다.
auto는  = 우측에 있는 expression 으로 그 타입을 추론하기 때문이다.

int i;  // no error. but, potentially uninitialized.
auto a; // compile error. can't deduce 'auto' type (initializer required)

auto b = 10; // OK

2. 길이가 긴 타입을 줄여서 사용 할 수 있다.

auto를 사용하면 나중에 그 Code를 보았을 때 한번에 무슨 타입인지 알기 힘들어서 사용하지 말자고 하시는 분들도 많다.
하지만 타입자체가 쓰기에는 너무 복잡하고, Code 앞뒤를 보았을때 한번에 유추가 가능한 경우는,
그 타입을 일일히 다 쓰는것 보다는 auto를 사용하는게 읽기 더 편할 수 있다.

template<typename T>
void Enumelator(std::vector<T> VEC)
{
for (std::vector<T>::iterator IT = VEC.begin(); IT != VEC.end(); IT++)
{
typename std::iterator_traits<std::vector<T>::iterator>::value_type current = *begin;
...
}

}

template<typename T>
void Enumelator(std::vector<T> VEC)
{
for (auto IT = VEC.begin(); IT != VEC.end(); IT++)
{
auto current = *begin;
...
}

}

위 2가지 Code 중 뭐가 더 읽기 편하고, 쓰기 편한가 ?
아래 auto를 쓴 Code를 보고 과연 저 auto가 어떤 타입일지 해깔려서 Code 읽기가 힘든가 ?
그렇다면야 항복 !!! 알아서 하세요.
저의 경우는 아래가 훨씬 쓰기도 읽기도 편하네요.

3. named Lambda를 선언 할때 편리하다.

auto CompareUniquePtr = []
    (const std::unique_ptr<Widget>& P1,
     const std::unique_ptr<Widget>& P2)
    { return *P1 < *P2; };

auto를 사용하지 않고 std::function을 사용해서 선언을 하면 아래와 같이 된다.

std::function<bool(const std::unique_ptr<Widget>&,
                   const std::unique_ptr<Widget>&)>
    CompareUniquePtr = [](const std::unique_ptr<Widget>& P1,
                          const std::unique_ptr<Widget>& P2)
    { return *P1 < *P2; };

심지어 같은걸 반복해서 써야 한다.
C++14에서는 autoLambda의 인자로도 사용이 가능해서 더 짧아진다.

auto CompareUniquePtr = [](const auto& P1, const auto& P2) { return *P1 < *P2; };

4. 쓸데없는 Code 수정을 줄일 수 있다.

함수의 return 타입이 컴파일하는 환경에 따라 달라지는 경우,
나중에 Code를 수정하여서 return 타입을 다르게 해야 할 경우,
해당 return 타입을 저장하는 변수를 auto로 선언하면 나중에 수정해야 할 작업을 줄일 수 있다.

std::vector<int> VEC;
unsigned int size = VEC.size();

std::vector의 size()는 x86/x64에서 크기가 각각 32 Bit, 64 Bit로 다르다.
하지만 unsigned int는 x86/x64 둘 다 32 Bit의 크기를 가진다.

auto size = VEC.size();

이와 같이 auto로 선언하면 Porting 시 발생 할 문제를 미리 예방 할 수 있다.

5. 쓸데없는 형변화 Cost를 감소 시킬 수 있다.

std::unordered_map<std::string, int> UM;
 ...
for (const std::pair<std::string, int>& P : UM)
{
...
}

위 Code를 한 번 보자. range-based for 문도 사용한 멋진 문장이다.
const auto&로 써도 되는 것을 친절하게도 const std::pair<std::string, int>& 로 썼다.
친절한건 감사한데요. 님아~ 틀렸어용.

for (const auto& P : UM)
{
...
}

이렇게 써놓고 auto나 P 위에 마우스를 살포시 올려보자.
Visual Studio가 아주 친절하게

const std::pair<const std::stringint>& P 

라고 알려준다.
겨우 const 하나 가지고 뭘 ?
저거 하나 때문에 for-loop를 돌면서
1. std::pair<const std::string, int>타입의 개체를 임시로 만듬.
2. std::pair<const std::string, int>에서  std::pair<std::string, int>개체로 Converting.
3. std::pair<const std::string, int> 개체 지움
의 과정을 계속 반복한다.
이 Cost가 만만하게 보이는가 ?
어찌보면 C++에서 가장 피해야할 Cost가 임시로 생성하고, 복사하고, 지우는 작업이다.

STL이 내부적으로 어떻게 구현되어 있는지 완벽하게 아는가 ?
아니면 특정 라이브러리를 사용할 때 어떻게 구현되었는지 완벽하게 아는가 ?
자기가 만든 라이브러리 같이 완벽하게 아는 경우에는 auto대신 구체적인 타입을 적어줘도 무리없겠지만,
100% 그런 확신이 없다면 그냥 auto를 쓰자.
혹시나 모를 Cost를 줄일 수 있다.


Things to Remember

auto는 반드시 초기화시켜 주어야 한다. 이로 인해 나중에 발생 할 수 있는 오류를 미리 예방가능하다.
* 잘못된 타입 지정 예방이 가능하며, Code를 효율적으로 짤 수 있게 된다.
* Refactoring 이 쉬워지며, Typing 횟수도 줄여 준다.

* 하지만 auto도 완벽한건 아니다. 발생 할 수 있는 실수에 대해서는 item 2 item 6을 참조하자.