iterator vs const_iterator

Modern C++에 적응하기
Effective Modern C++ Chapter 3. iterator vs const_iterator


iterator vs const_iterator

const를 사용하는 것이 의미가 있는 경우에는 항상 const를 사용하는 것이 바람직하다. iterator도 마찬가지이다.


C++98에서 const_iterator는 사용하기가 상당히 까다로웠다.
먼저 non-const 컨테이너로부터 const_iterator를 얻는 간단한 방법이 없었다.

1
2
3
4
5
6
7
8
9
10
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()),
1983);

valuesint(non-const)타입의 데이터를 저장하는 컨테이므로, values.begin()values.end()iterator를 반환한다.
std::find함수에서 데이터 수정이 발생하지 않을 것이므로 iterator보다는 const_iterator를 인자로 전달하는 것이 의미론적으로 옳을것이다.
그렇기 때문에 std::findconst_iterator를 전달하기 위해서 캐스팅이 필요하다.


C++98에서 STL 컨테이너의 삽입(insert), 삭제(erase)함수는 인자로 오직 iterator만 사용할 수 있었다.

1
values.insert(static_cast<IterT>(ci), 1998); // error

C++11에 들어서 두 함수가 const_iterator를 지원하기 시작했지만, const_iterator에서 iterator로의 적절한 변환이 존재하지 않는다.


C++11 에서는 const_iterator에 대한 편의성이 많이 개선되었다.
먼저 컨테이너가 직접 const_iterator를 반환하는 멤버함수 cbegin()cend()를 제공한다.
그리고 위에서 언급했듯이 STL 컨테이너의 삽입, 삭제 함수들이 const_iterator를 지원한다.

1
2
3
4
5
std::vector<int> values;
...

auto it = std::find(values.cbegin(), values.cend(), 1983);
values.insert(it, 1998);

C++14에서는 비멤버 함수 cbegin()cend()도 표준에 추가되었다.(rbegin(), rend(), crbegin(), crend()와 함께)

1
2
3
4
5
6
7
8
template <typename C, typename V>
void findAndInsert(C& container, const V& targetVal, const V& insertVal)
{
auto it = std::find(std::cbegin(container),
std::cend(container),
targetVal);
container.insert(it, insertVal);
}

위 템플릿 함수가 C++11에서도 동작하게 하기위해 직접 비멤버 cbegin()cend()를 구현할 수 있다.
C++11에서 직접 구현한 비멤버 cbegin()은 아래와 같다.

1
2
3
4
5
template <typename C>
auto cbegin(const C& container) -> decltype(std::begin(container))
{
return std::begin(container);
}

템플릿 매개변수 C에 일반적인 STL 컨테이너가 전달되었다면, 함수 매개변수 container는 해당 컨테이너의 const 참조로 연역된다.
그리고 그런 컨테이너에 비멤버 begin을 호출하면 const_iterator가 반환된다.


이 템플릿 함수는 STL 컨테이너 뿐만 아니라 내장 배열에 대해서도 잘 동작한다.
비멤버 begin은 이러한 내장배열에 특수화 되어있는데, 배열의 원소를 가리키는 포인터를 반환한다.
이 경우에 containerconst 배열에 대한 참조로 연역된다. const배열의 원소는 const이므로, std::beginconst를 가리키는 포인터를 반환한다.



References

Author

Joyus.Gim

Posted on

2022-07-26

Updated on

2022-07-26

Licensed under

Comments