본문으로 바로가기

STL) 반복자 어댑터 ( Iterator Adaptor )

category C++/STL 2019. 4. 12. 01:43

std::copy, std::transform, etc...

대부분의 Dest의 Iterator를 받는 알고리즘들은 그 컨테이너가 공간 요소를 넣기에 충분한 용량 ( capacity ) 를 확보하길 요구한다.

하지만 얼마나 들어올지 모르는 상황에서 컨테이너에 대한 reserve를 할 수 없으니, 반복자 어댑터를 사용하는걸 추천한다.

std::copy, std::transform 같은 알고리즘들은 밑과 같이 작성되어 있다.

template<class InputIt, class OutputIt>
OutputIt copy(InputIt first, InputIt last, 
              OutputIt d_first)
{
    while (first != last) {
        *d_first++ = *first++;
    }
    return d_first;
}

iterator 의 operator= 를 통해 복사 또는 연산을 지원하는데, 반복자 어댑터는 이 iterator의 operator= 를 

operator overloading 하여 내부에서 해당 컨테이너의 push 연산을 호출한다.

 

template<class _Container>
class back_insert_iterator : public _Outit
{    // wrap pushes to back of container as output iterator
public:
    explicit back_insert_iterator(_Container& _Cont)
        : container(&_Cont)
    {    // construct with container
    }

    back_insert_iterator<_Container>& operator=(
        typename _Container::const_reference _Val)
    {    // push value into container
        container->push_back(_Val);	// back_insert_iterator 는 container 내의 push_back을 호출
        return (*this);
    }

protected:
    _Container *container;    // pointer to container
};

inserter(), back_inserter(), front_inserter() 는 각각

insert_iterator, back_insert_iterator, front_insert_iterator 를 생성한다. 

물론 Container에 맞는 iterator adaptor function을 호출하여야 한다.

 

inserter()는 내부에서 해당 컨테이너의 insert 함수를 호출한다.

insert_iterator& operator=(typename _Container::value_type&& _Val)
{	// push value into container
	iter = container->insert(iter, _STD move(_Val));
	++iter;
	return (*this);
}

 

	std::list<int> list1{ 1,2,3,4,5 };
	std::list<int> examlist1;
	std::copy(list1.begin(), list1.end(), std::back_inserter(examlist1));
	// 1, 2, 3, 4, 5 -> 내부에선 push_back을 호출함

	std::list<int> examlist2;
	std::copy(list1.begin(), list1.end(), std::front_inserter(examlist2));
	// 5, 4, 3, 2, 1 -> 내부에선 push_front를 호출함

	std::list<int> examlist3;
	std::copy(list1.begin(), list1.end(), std::inserter(examlist3, examlist3.begin()));
	// 1, 2, 3, 4, 5 -> 내무에선 examlist3.insert(list1.begin(), list1.begin() +1... 이 될 것)

물론 inserter 같은 function이 아닌, iterator 자체를 만들어 넣어줄 수도 있다.

 

std::list<int> examlist4;
std::copy(list1.begin(), list1.end(), std::back_insert_iterator<std::list<int>>(examlist4));

std::list<int> examlist5;
std::copy(list1.begin(), list1.end(), std::insert_iterator<std::list<int>>(examlist4, examlist4.begin()));

 

모든 inserter_iterator 들은 nothrow 하다.