KoreanFoodie's Study

Effective Modern C++ | 항목 13 : iterator 보다 const_iterator 를 선호하라 본문

Tutorials/C++ : Advanced

Effective Modern C++ | 항목 13 : iterator 보다 const_iterator 를 선호하라

GoldGiver 2022. 10. 26. 09:53

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

항목 13 : iterator 보다 const_iterator 를 선호하라

핵심 :

1. iterator 보다 const_iterator 를 선호하라.
2. 최대한 일반적인 코드에서는 begin, end, rbegin 등의 비멤버 버전들을 해당 멤버 함수들보다 선호하라.


C++98 에서는, const_iterator 사용이 쉽지 않았다.

typedef std::vector<int>::iterator IterT;
typedef std::vector<int>::const_iterator ConstIterT;

std::vector<int> values;

...

ConstIterT ci =
  std::find(
    static_cast<ConstIterT>(values.begin()),
    static_cast<ConstIterT>(values.end()),
    value_to_find);

values.insert(static_cast<IterT>(ci), value_to_modify);

위에서 Iterator 에서 const_iterator 로의 캐스팅을 통해 ci 를 받아내고 있는 과정에서 다소 작위적인 왜곡이 가해지고 있다. 또한, C++98 에서는 insert / remove 시 iterator 만 사용할 수 있었기 때문에, const_iterator 에서 iterator 로의 캐스팅을 시도하고 있는데, 해당 변환은 이식성 있는 변환이 존재하지 않아 컴파일되지 않을 수 있다!
대신 C++11 에서는 다음과 같이 코드를 고쳐서 정상 작동하도록 만들 수 있다.

auto it = 
  std::find(values.cbegin(), values.cend(), value_to_find);

values.insert(it, value_to_modify);


위와 같은 코드가 문제가 생기는 경우는, begin 이나 end 사용 시, 멤버 함수를 사용해서 생길 수 있는 라이브러리 호환 관련 경우 말고는 찾기 힘들다. 즉, 다음과 같이 바꾸면 더 일반화된 경우를 지원할 수 있다.

template<typename C, typename V>
void findAndInsert(C& container,
  const V& targetVal,
  const V& insertVal)
{
  using std::cbegin;
  using std::cend;

  auto it = std::find(cbegin(container), cend(container), targetVal);

  container.insert(it, insertVal);
}

위 코드는 C++14 에서는 잘 동작하지만, C++11 에서는 비멤버 버전의 cbegin, cend, rbegin, rend, crbegin, crend 는 추가되지 않아, 아래와 같이 직접 구현해야 한다.

template<class C>
auto cbegin(const c& container)->decltype(std::begin(container))
{
  return std::begin(container);
}

놀랍게도, 비멤버 cbegin 은 멤버 cbegin을 호출하지 않는다. 이렇게 구현한 이유는, begin 만 직접적으로 제공하고, cbegin 을 제공하지 않는 컨테이너에 대해서도 사용자가 만든 비멤버 cbegin 을 사용할 수 있기 때문이다.

Comments