일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 인공지능
- pwnable.kr
- Python
- AI
- Algorithm
- Linear_regression
- logistic regression
- leg
- 기능개발
- Softmax classification
- 텐서플로
- programmers
- tensorflow
- Today
- Total
나혼자 공부장
[Modern Effective C++] Chap 2. auto 본문
Item 5: Prefer auto to explicit type declarations.
명시적 타입 선언보다는 auto를 선호하라
1. 왜 auto인가?
1) 이식성 또는 효율성 문제를 유발할 수 있는 타입 불일치가 발생하는 경우가 거의 없다.
2) 대체로 변수의 타입을 명시적으로 지정할 때보다 타자량이 적다.
단, 반드시 초기화해야함
template <typename It> // b에서 e까지의 구간에 있는 모든
void dwim(It b, It e) // 요소에 대해 dwim("do what I mean")
{ // 알고리즘을 수행한다.
for (; b != e; ++b) {
typename std::iterator_traits<It>::value_type
currValue = *b;
...
}
}
// auto를 사용하면 훨씬 간단하게(적은
// 타이핑으로) 선언할 수 있다.
template <typename It>
void dwim(It b, It e)
{
for (; b != e; ++b) {
auto currValue = *b;
...
}
}
위와 같이 훨씬 간단한 코드를 구현할 수 있다.
여기서 첫 번째 템플릿 안에 있는 typename이 무엇인가?
T::iterator.. 처럼 중첩 의존문을 사용해야 할 때 쓰인다. (매개변수의 타입에 따라 그에 의존한 타입도 달라지는 구문) C++ 에서는 중첩 의존을 타입으로 취급하지 않으므로, typename으로 그것이 타입이라고 식별하게끔 컴파일러에게 알려주는 것이다.
auto derefUPLess = [](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2)
{return *p1 < *p2;}
auto derefUPLess = [](const auto& p1, const& p2) {return *p1 < *p2;}
위처럼 람다함수의 매개변수로도 사용할 수 있다.
람다함수 설명은 아래 블로그에 잘 되어있다.
모두의 코드
C 언어 문법을 아시는 분들이라면, 씹어먹는 C++ 강좌를 통해 C++ 기초 부터 최근의 C++ 17 까지 모든 내용을 배우실 수 있습니다. C 언어와 C++ 의 기본적인 문법이 비슷하기 때문에, C 언어를 어느 정도 아는 독자를 가정하여 쓰여져 있습니다. 현재 강좌는 연재 진행 중이며, 총 44 개의 강좌가 준비되어 있습니다.
modoocode.com
씹어먹는 C++ 토막글 ② - 람다(lambda) 함수
이 글은 http://ciere.com/cppnow12/lambda.pdf 에서 가져왔고 한국말로 번역되었습니다. 또한 저의 개인적인 C++ 능력 향상과 ' 저의 모토인 지식 전파'를 위해 모든 이들에게 공개하도록 하겠습니다.이 글을 이해하기 위해서는 초보 이상의 C++ 지식이 필요합니다. 아직 C++ 에 친숙하지 않다면 [씹어먹는 C++ 강좌](http://itguru.tistory.com/135)는 어때요?안녕하세요? 이 글은 지난번에 우측값 레퍼런스
modoocode.com
2. std::function 보다 뭐가 더 유리한가?
std::function은 템플릿의 한 인스턴스이며, 그 크기는 임의의 주어진 시그니처에 대해 고정되어 있다. 유동적이지가 않다.
그 크기가 요구된 클로저를 저장하기에 부족할 경우에는 힙 메모리를 할당한다
-> auto로 선언된 객체보다 훨씬 많은 메모리를 소비한다.
-> 매개변수의 타입을 하나하나 반복해서 지정해주지 않아도 된다.
아래 코드만 봐도 얼마나 장황하게 구현되는지 알 수 있다.
std::function<bool(const std::unique_ptr<Widget>&,
const std::unique_ptr<Widget>&)>
derefUPLess = [](const std::unique_ptr<Widget>& p1,
const std::unique_ptr<Widget>& p2)
{ return *p1 < *p2;
3. 운영체제에서 자유롭다.
아래와 같이 선언하는 개발자들이 많다.
그러나 32bit Windows에서 unsigned의 크기와 std::vector<int>::size_type은 같지만
64bit Windows 환경에서는 unsigned가 32bit인 반면 std::vector<int>::size_type 이 64bit이다.
이는 운영체제에 따라 잘 동작하지 않을 수도 있다는 치명적인 단점이다.
auto가 이에 해결책을 제공한다.
std::vector<int> v;
...
unsigned sz = v.size()
auto sz = v.size() // 바람직한 선언
4. auto는 객체 타입 식별을 어렵게 한다?
물론 코드만 봐서는 그렇긴 하다. 그러나 객체의 타입을 보여주는 IDE의 기능으로 이는 완화될 수 있다.
그리고 변수 이름만 잘 지어둬도 추상적인 타입 정보는 거의 항상 바로 파악할 수 있다.
Item 6: Use the explicitly typed initializer idiom when auto deduces undesired types.
auto가 원치 않은 타입으로 추론될 때에는 명시적 타입의 초기치를 사용하라
1. "보이지 않는" 프록시 타입 때문에 auto가 초기화 표현식의 타입을 "잘못" 추론할 수 있다.
프록시 패턴이 무엇인가?
swift로 치면 delegate 같은 개념이다. 간단하게 말하자면 어떤 객체에 접근하는데 대리 객체를 제공하는 것이다.
shared_ptr, unique_ptr 과 다르게 사용자에게 보이지 않도록 설계된 프록시 클래스들이 있다.
이런 클래스들은 기본적으로 auto와 잘 맞지 않는다.
std::vector<bool> features(const Widget& w);
Widget w;
...
bool highPriority = features(w)[5]; // w의 우선순위가 높은가?
...
processWidget(w, highPriority); // w를 그 우선순위에 맞게
// 처리한다.
auto highPriority = features(w)[5]; // w의 우선순위가 높은가?
// 여전히 컴파일되지만, 이제는
// 그 행동을 예측할 수 없다.
processWidget(w, highPriority); // 미정의 행동!
위와 같이 auto로 선언했을 경우에 미정이 되는 이유는 vector<bool>::operator[] 가 bool&을 반환하지 않고,
std::vector<bool>::reference라는 프록시 객체를 반환하여, 객체의 수명이 한 문장 이상으로 연장되지 않도록 만들어지는 것이 보통이기 때문이다.
즉, processWidget이 호출될 시점에는 이미 HighPriority 객체 내부의 포인터는 이미 소멸된 대상을 가리키고 있게 된다.
그럼 이 프록시 객체의 존재는 어떻게 확인하는가?
라이브러리 문서, 헤더 파일 등등 숨겨진 프록시 객체라 하더라도 그 존재를 완전히 숨길 수 있는 경우는 거의 없다.
2. 타입 명시 초기치 관용구는 auto가 원하는 타입을 추론하도록 강제한다.
위에서 설명한 경우에는 auto가 도움이 되지 않았다.
이때 해결책은 auto를 버리는 것이 아니다. static_cast 등으로 auto가 우리가 원했던 타입을 추론하도록 강제할 수 있다.
std::vector<bool> features(const Widget& w);
Widget w;
...
auto highPriority = features(w)[5];
// features의 반환 타입은 std::vector<bool>
// 이고 std::vector<bool>::operator[]의
// 반환 타입은 std::vector<bool>::reference
// 인데, 우리가 필요한 것은 bool 타입이다.
// 다음은 highPriority가 bool로 추론되도록
// 강제하는 예이다.
auto highPriority = static_cast<bool>(features(w)[5]);
저자는 auto를 버리는게 해결책이 아니라고는 했지만 개인적으로는 그렇게까지 auto만을 고집해야할 이유는 모르겠다.
프록시 객체로 문제가 되는 상황이라면 굳이굳이 형변환을 하기보다 명시적으로 타입을 선언하는게 훨씬 단순하지 않을까 싶다.
'C++ > Modern Effective C++' 카테고리의 다른 글
[Modern Effective C++] Chap 3. 현대적 C++에 적응하기 (0) | 2020.02.24 |
---|---|
[Modern Effective C++] Chap 1. Deducing Types (타입 추론) (0) | 2020.02.10 |