Effective C++/Effective STL
[Effective STL] 30. 알고리즘 사용 시 목적지 범위(destination range) 주의
김디트
2024. 12. 17. 10:57
728x90
항목 30. 알고리즘의 데이터 기록 범위(destination range)는 충분히 크게 잡자
컨테이너 요소 삽입 시
int transmogrify(int x); // 정수 x를 받아 어떤 새로운 값을 만드는 함수
vector<int> values;
... // values에 데이터를 넣는다.
// values 요소들에 transmogrify를 적용하고 result에 삽입한다.
// 하지만, 버그가 있다.
vector<int> results;
transform(values.begin(), values.end(), results.end(), transmogrify);
- transform 결과 기록 순서
- values[0]을 매개변수로 가져온다.
- transmogrify를 호출하여 값을 변환한다.
- *results.end()에 대입한다.
- values[1]에 대해 1번부터 다시 진행
- 허나, 이 시점에서 results에는 공간이 할당되어 있지 않다.
- 즉, 목적지 범위인 results.end()에는 아무런 객체가 없다.
- 알고리즘이 알아서 컨테이너에 insert하며, 컨테이너가 공간을 할당할 것이라 기대했지만..
- 그렇게 하려면 STL에게 똑바로 알려줘야 한다.
vector<int> results;
transform(values.begin(), values.end(), back_inserter(results), transmogrify);
// 이 경우, back_inserter는 push_back을 호출하게 되어 있으므로,
// 대상 컨테이너는 반드시 push_back을 지원해야 한다.
results 앞에다가 transform 결과를 추가하고 싶다면?
- 물론 그냥 front_inserter를 사용할 수 있지만..
- 객체들을 순서대로 각각 push_front 시키므로 순서가 반대로 삽입된다.
- vector는 push_front를 지원하지 않는다.
- values를 뒤집어서 넣어주면 된다.
list<int> results;
transform(values.rbegin(), values.rend(), front_inserter(results), transmogrify);
임의의 위치를 지정하여 결과를 추가하고 싶다면?
- 반복자 어댑터를 사용하면 된다.(항목 5 참조)
vector<int> results;
... // 데이터가 몇 개 들어있다.
transform(values.begin(), values.end(),
inserter(results, results.begin() + results.size() / 2), // 중간에 삽입
transmogrify);
오버헤드 줄이기
- 이 모든 inserter들은 한번에 한 개의 요소만 삽입하는데, 이는 오버헤드이다.(항목 5 참조)
- 하지만, transform은 어쨌든 한 번에 하나의 값을 목적지 범위에 저장하기에 바꿀 수 있는 방법은 없다.
- 대상 컨테이너가 vector, string이라면 reserve를 호출하여 오버헤드를 최소화할 수 있다.(항목 14 참조)
vector<int> values;
vector<int> results;
...
reuslts.reserve(results.size() + values.size()); // 공간 확보
transform(values.begin(), values.end(),
inserter(results. results.begin() + results.size() / 2),
transmogrify);
- reserve는 컨테이너의 요소 크기가 아니라 용량(capacity)만을 증가시킨다는 사실을 기억할 것.
- 그러므로 reserve를 호출한 후에도 반복자 어댑터를 사용해야 한다.
vector<int> values;
vector<int> results;
...
results.reserve(results.size() + values.size());
// transform(values.begin(), values.end(), results.end(), transmogrify);
// reserve를 했더라도 위 코드는 미정의 동작!!
// 아래처럼 해야 한다.
transform(values.begin(), values.end(), back_inserter(results), transmogrify);
새 요소가 아니라 원래의 요소를 덮어쓰고 싶은 경우?
- 방법 1 : resize로 크기를 확보한다.
vector<int> values;
vector<int> results;
...
if(results.size() < values.size())
{
// results의 크기를 values의 크기만큼으로 맞춘다.
results.resize(values.size());
}
// values를 results에 덮어쓴다.
transform(values.begin(), values.end(), results.begin(), transfmogrify);
- 방법 2 : results를 clear한 후 반복자 어댑터를 사용한다.
...
results.clear(); // 요소 모두 삭제
results.reserve(values.size());
transform(values.begin(), values.end(), back_inserter(results), transmogrify);
728x90