KoreanFoodie's Study

Effective Modern C++ | 항목 5 : 명시적 형식 선언보다는 auto 를 선호하라 본문

Tutorials/C++ : Advanced

Effective Modern C++ | 항목 5 : 명시적 형식 선언보다는 auto 를 선호하라

GoldGiver 2022. 10. 26. 09:50

C++ 프로그래머의 필독서이자 바이블인, 스콧 마이어스의 Modern Effective C++ 를 읽고 기억할 내용을 요약하고 있습니다. 꼭 읽어보시길 추천드립니다!

항목 5 : 명시적 형식 선언보다는 auto 를 선호하라

핵심 :

1. auto 변수는 반드시 초기화 해야 하며, 이식성 또는 효율성 문제를 유발할 수 있는 형식 불일치가 발생하는 경우가 거의 없으며, 대체로 변수의 형식을 명시적으로 지정할 때보다 타자량도 더 적다.
2. auto 로 형식을 지정한 변수는 항목 2 와 항목 6 에서 설명한 문제점들을 겪을 수 있다.


아래 예시를 보면, auto 를 쓰는 것이 여러모로 훌륭하다는 것을 한눈에 파악할 수 있을 것이다.

template<typename It>
void print_from_b_to_e(It b, It e)
{
	for (; b != e; ++b)
	{
		typename std::iterator_traits<It>::value_type
			currValue = *b;
		std::cout << currValue << std::endl;

		// auto 를 쓰면 훨씬 간단하다!
		auto currValue2 = *b;
		std::cout << currValue2 << std::endl;
	}
}


또한, auto 는 예전에는 컴파일러만 알던 형식을 지정할 수도 있고, 람다 표현식의 매개변수에도 사용할 수 있다.

std::function<bool(const std::unique_ptr<Widget>& p1,
	const std::unique_ptr<Widget>& p2)>
 derefUPLess_bad =
	[] (const std::unique_ptr<Widget>& p1,
			const std::unique_ptr<Widget>& p2)
	{ return *p1 < *p2; };


// std::unique_ptr 들이 가리키는 Widget 객체들을 비교
auto derefUPLess =
	[] (const std::unique_ptr<Widget>& p1,
			const std::unique_ptr<Widget>& p2)
	{ return *p1 < *p2; };

// C++14 버전 :
// 그 어떤 것이든 포인터처럼 작동하는 것들이 가리키는 값들을 비교
auto derefUPLess2 =
	[] (const auto& p1,
			const auto& p2)
	{ return *p1 < *p2; }	;


또한 auto 는 "형식 단축(type shortcut)" 과 관련된 문제를 피할 수 있다. 예시를 보자.

std::vector<int> v;
// v.size() 의 공식적인 반환 형식은 std::vector<int>::size_type 임
unsigned sz = v.size();
// 하지만 64 비트 Windows 에서는 unsinged 가 32 비트인 반면,
// std::vector<int>::size_type 은 64 비트로, 오작동 가능성이 있다!

// 이식성 문제 해결
auto sz2 = v.size();

이는 이식성과도 연관이 된다.

마지막으로, auto 를 사용하지 않으면 오히려 안되는 경우를 보자.

std::unordered_map<std::string, int> m;
for (const std::pair<std::string, int>& p : m)
{
    // do something
}

위 코드에서, 키 부분에 const 가 붙었으므로, 해시 테이블에 담긴 std::pair 의 형식은 std::pair<std::string, int> 가 아니라 std::pair<const std::string, int> 이다. 컴파일러는 std::pair<std::string, int> 객체를 std::pair<const std::string, int> 객체들로 변환하려 든다. 각 반복에서 컴파일러는 p 형식의 임시 객체를 생성하고, m 의 각 객체를 복사하고, 참조 p 를 그 임시 객체에 묶음으로써 그러한 변환을 실제로 수행한다. 그 임시 객체는 루프 반복의 끝에서 파괴된다.
auto 를 사용하면 어떨까?

std::unordered_map<std::string, int> m;
for (const auto& p : m)
{
    // do something
}

p 의 주소는 실제로 m 안의 한 요소를 가리키는 포인터의 주소이다. 이는 효율적이고 타자도 더 쉽다.

Comments