스토리텔링 개발자

[Effective Modern C++] 13. const_iterator 선호하기 본문

Effective C++/Effective Modern C++

[Effective Modern C++] 13. const_iterator 선호하기

김디트 2025. 2. 21. 10:57
728x90

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

 

 

 

const_iterator
  • const_iterator는 수정하면 안되는 값들을 가리킨다.
  • 가능한 한 const를 사용하라는 표준 관례는 반복자에도 적용된다.
  • 허나 C++98에서는 이게 쉽지 않았다.(EC++ 항목 26 참조)
std::vector<int> values;
...
std::vector<int>::iterator it = std::find(values.begin(), values.end(), 1983);
values.insert(it, 1998);
  • 위 코드에서의 반복자는 전혀 수정되지 않으므로 const_iterator면 좋겠다.
  • 하지만 C++98에서는 간단하지 않다.
typedef std::vector<int> 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()),
                          1983);
values.insert(static_cast<IterT>(ci), 1998); // 컴파일이 안 될 수 있다.
  • static_cast를 사용하여 다소 작위적인 코드가 되었다.
    • 비 const 컨테이너로부터 const_iterator를 얻는 간단한 방법 없기 때문이다.
  • C++98에서는 삽입 / 삭제 위치를 iterator로만 지정할 수 있으므로 iterator로 다시 캐스팅해야 했다.
    • 허나 const_iterator에서 iterator로의 이식성 있는 변환은 존재하지 않으므로 컴파일 에러가 발생한다.

 

 

 

C++11에서의 const_iterator
  • 얻고 사용하기 쉬워졌다.
  • 컨테이너 멤버 함수 cbegin, cend는 const_iterator를 돌려준다.
  • 삽입, 삭제 위치를 지정 STL 멤버 함수들이 이제는 const_iterator도 받는다.
std::vector<int> values;
...

auto it = std::find(values.cbegin(), values.cend(), 1983); // cbegin / cend 사용
values.insert(it, 1998);
  • 최대한 범용적인 라이브러리 코드(maximally generic)를 작성할 때는 문제가 있다.
    • 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); // 비멤버 cbegin / cend 사용
    container.insert(it, insertVal);
}
  • 위 코드는 C++14에선 동작하지만. C++11에선 안된다.
    • 비멤버 begin, end만 존재하기 때문이다.
  • 아래처럼 직접 구현해주는 게 어렵진 않다.
template<class C>
auto cbegin(const C& container)->decltype(std::begin(container))
{
    return std::begin(container); // 멤버 cbegin을 호출하지 않고 구현?
}
  • 매개변수로 들어온 컨테이너가 const &에 std::begin를 사용하면 const_iterator 타입이 리턴된다.
  • 멤버 cbegin을 사용하지 않고 구현한 덕분에 cbegin이 없는 컨테이너에 대해서도 동작한다.
  • 기본 배열 타입에 대해서도 동작한다.
    • 이 경우 const 배열에 대한 참조가 된다.
    • C++11의 비멤버 begin엔 기본 배열 타입 템플릿 특수화 버전이 존재한다.
728x90
Comments