-
[Effective Modern C++] const_iteratorC++/Effective Modern C++ 2022. 4. 27. 13:39
const_iterator는 문자 그대로 const한 iterator입니다.
const를 사용하는게 가능하다면 const를 사용하는 것은 iterator에도 적용이 되어, 값을 수정할 필요가 없을 때에는 const_iterator를 쓰는 것이 바람직합니다.
하지만, C++11 이전에는 이 const_iterator를 사용하는 것이 조금 난해했습니다.
이번 글에서는 const_iterator에 대하여 다루어 보겠습니다.
std::vector<int>에서 1이라는 값을 찾고 그 앞에 2를 삽입하는, 혹은 값이 존재하지 않을 경우 벡터의 끝에 2를 삽입하는 코드는 아래와 같이 작성할 수 있습니다.
std::vector<int> vec; ... std::vector<int>::iterator it = std::find(vec.begin(), vec.end(), 1); vec.insert(it, 2);
위 코드의 경우 iterator을 수정하지 않으므로, const_iterator를 사용하는 것이 마땅합니다.
아래 문단들에서 이어집니다.
1. const_iterator : C++98
더보기C++98에서는 const_iterator를 다음과 같이 사용할 수 있습니다.
typedef std::vector<int>::iterator vec_iter; typedef std::vector<int>::const_iterator vec_citer; std::vector<int> vec; ... vec_citer cit = std::find( static_cast<vec_citer>(vec.begin()), static_cast<vec_citer>(vec.end()), 1); vec.insert(static_cast<vec_iter>(cit), 2);
using대신 typedef를 사용한 것은, using은 C++11에 추가된 기능이기 때문입니다.
iterator를 사용한 코드보다 조금 복잡해졌는데, 더 큰 문제점은 이것이 컴파일이 되지 않을 수 있다는 것 입니다.
위 코드의 난해한, 어색한 부분들은 다음과 같습니다.
- C++98에서는 non-const 컨테이너로부터 const_iterator를 얻는 간단한 방법이 존재하지 않았기 때문에, 억지로 캐스팅을 했습니다.
- C++98에서는 insert및 erase의 위치를 지정할 때 const_iterator를 사용할 수 없었습니다.
- C++98에는 const_iterator에서 iterator로의 이식성 있는 변환이 존재하지 않습니다. 따라서, 위 코드는 컴파일 되지 않을 수 있습니다.
위와 같은 문제점들 때문에, C++98에서는 const_iterator를 사용하는 것이 실용적이지 않았습니다.
2. const_iterator : C++11
더보기위 문단의 코드를 C++11을 기준으로 수정하면 다음과 같이 사용할 수 있습니다.
std::vector<int> vec; ... auto cit = std::find( vec.cbegin(), vec.cend();, 1); vec.insert(cit, 2);
C++11부터 const_iterator를 얻는 것이 굉장히 간편해졌습니다.
컨테이너의 iterator반환 함수 begin, end의 const한 버전인 cbegin, cend를 통해 const_iterator를 얻을 수 있습니다.
또한 insert, erase등 위치 지정을 목적으로 iterator를 사용하는 멤버 함수들에 const_iterator를 사용할 수 있게 되었습니다.
3. iterator와 비 멤버 함수 (Non member function)
더보기begin, end와 같은 iterator를 반환하는 멤버 함수들은 비 멤버 함수로도 존재합니다.
이런 iterator를 다루는 비 멤버 함수들은 일반적인 코드 (ex. 라이브러리)를 작성할 때 유용합니다.
멤버 함수에 iterator가 없는 (ex. 내장 배열)자료구조에도 작동하는, 위 문단의 탐색 및 삽입 기능을 하나의 함수로 만들어보면 다음과 같이 할 수 있습니다.
template<typename C, typename V> void find_and_insert(C& container, const V& target_val, const V& insert_val) { auto cit = std::find( cbegin(container), cend(container), target_val); container.insert(cit, insert_val); }
위 코드의 cbegin, cend함수는 const_iterator를 반환하는 비 멤버 함수입니다.
하지만, const하거나 참조 형식의 iterator를 반환하는 cbegin, cend, rbegin, rend함수들은 C++11이 아닌 C++14에서 추가되었습니다.
위 함수를 C++11에서 사용하기 위해 cbegin, cend를 직접 구현할 수 있는데, 다음과 같이 할 수 있습니다.
template<typename C> auto cbegin(const C& container) -> decltype(std::begin(container)) { return std::begin(container); }
begin을 이용해 cbegin을 구현했지만, begin이 참조하는 매개변수가 const하기 때문에 const_iterator가 반환됩니다.
수정되지 않는, 혹은 수정되지 않아야 하는 변수들은 const로 선언하는 것이 좋습니다.
하지만, 본문의 예제와 같이 C++98에서는 iterator에 대하여 위 사항을 지키는 것이 난해했습니다.
이러한 문제들이 C++11, C++14로 발전하면서 해결되었으니 const_iterator를 사용할 수 있는 상황이라면 사용하는 것이 좋겠습니다.
감사합니다.
'C++ > Effective Modern C++' 카테고리의 다른 글
[Effective Modern C++] constexpr (0) 2022.04.29 [Effective Modern C++] noexcept (0) 2022.04.28 [Effective Modern C++] 식별자 : override (0) 2022.04.22 [Effective Modern C++] private와 delete (0) 2022.04.22 [Effective Modern C++] enum과 enum 클래스 (0) 2022.04.18