나만의 작은 도서관
[C++][STL Algorithm] vector 부분 복사에 유용한 std::copy(feat. inserter 계열 클래스) 본문
C++/문법 및 메소드(STL)
[C++][STL Algorithm] vector 부분 복사에 유용한 std::copy(feat. inserter 계열 클래스)
pledge24 2026. 1. 9. 18:31std::copy란?
template <class InputIt, class OutputIt>
OutputIt copy(InputIt first, InputIt last, OutputIt d_first);
- std::copy란 지정한 범위의 메모리를 복사하여 다른 위치에 붙여 넣는 함수로, 3개의 iterator를 인자로 받으며 [first, last) 범위의 메모리를 복사하여 d_first부터 붙여 넣는다.
- 다른 STL Algorithm 함수들과 동일하게 last 위치는 범위에 포함시키지 않기 때문에 std::copy의 last 위치의 메모리가 복사 범위에 들어가지 않음을 유의해야 한다.
- std::copy를 사용할 수 있는 범위가 굉장히 넓지만, 제목에서 알 수 있듯 자주 사용되는 상황은 vector 부분 복사이다. 예제는 아래와 같다.
vector<int> v1 = {1, 2, 3, 4, 5};
int first = 1;
int last = 3;
vector<int> v2(last-first+1);
std::copy(v1.begin()+first, v1.begin()+last+1, v2.begin());
for(const int& elem : v2)
{
cout << elem << ' ';
}
cout << '\\n';
// 실행 결과
2 3 4
std::copy는 반복자(iterator) 기능이 있는 모든 클래스에서 사용 가능하다.
- std::copy는 컨테이너에 종속된 함수가 아니라 iterator를 중점으로 로직이 돌아가기 때문에 iterator 기능을 넣어놓은 클래스라면 사용이 가능하다.
- STL의 컨테이너(단, queue, stack과 같은 컨테이너 어댑터는 제외)들은 iterator를 지원하기 때문에 전부 사용이 가능하다. 나열하면 아래와 같다.
- 시퀀스 컨테이너: std::vector, std::list, std::deque, std::array, std::forward_list 등.
- 연관/무순서 컨테이너: std::set, std::map, std::unordered_set 등 (주로 이 컨테이너들의 데이터를 다른 곳으로 복사할 때 사용).
- C 스타일 배열: 일반 배열의 포인터도 반복자의 역할을 하므로 사용 가능.
- 기타: std::string, std::valarray 등.
for문 방식과 비교했을 때의 장점
// 1. 기본 방식
{
vector<int> v2;
for(int i = first; i <= last; i++)
{
int num = v1[i];
v2.push_back(num);
}
}
// 2. std::copy() 사용 방식
{
vector<int> v2(last-first+1);
std::copy(v1.begin()+first, v1.begin()+last+1, v2.begin());
}
- 1번 방식과 2번 방식 둘 다 v1의 first부터 last까지의 원소를 v2에 복사하는 코드이다. 1번 방식인 for문 활용 코드도 코드 길이가 길지 않기 때문에 충분히 좋은 방식이지만, 2번 방식인 std::copy() 활용 코드가 한 줄의 코드로 복사를 하는 코드임을 명시할 수 있기 때문에 1번 방식보다 가독성이 높고 코드 길이도 짧다.
주의 사항: 붙여 넣을 위치에 메모리가 부족하면 오류 발생!
{
vector<int> v2;
std::copy(v1.begin()+first, v1.begin()+last+1, v2.begin());
}
- std::copy는 복사 붙여 넣기를 할 뿐, 붙여 넣을 위치에 메모리가 부족했을 때 알아서 메모리를 확보하지는 않는다. 그렇기 때문에 위 코드처럼 메모리를 충분히 확보하지 않은 벡터 v2의 begin()을 그대로 3번째 인자에 넣게 되면 오류가 발생하게 된다.
- 그렇다면 매번 std::copy를 쓸 때마다 붙여 넣을 위치에 충분한 메모리가 확보되었는지 확인해야 할까? 이러한 확인 작업은 매우 귀찮기도 하고, 놓치기도 쉽기 때문에 최대한 안 하고 싶을 텐데, 이럴 때는 알아서 메모리 공간을 확보하는 inserter 계열의 클래스를 같이 사용하면 된다.
// 2-1. std::copy() + back_inserter 사용 방식
// back_inserter를 인자로 넘겨주면 미리 메모리 확보를 하지 않아도 된다!
{
vector<int> v2;
std::copy(v1.begin()+first, v1.begin()+last+1, back_inserter(v2));
}
inserter 계열의 클래스?
// std::back_inserter
template<class Container>
std::back_insert_iterator<Container> back_inserter(Container& c)
{
return std::back_insert_iterator<Container>(c);
}
// std::front_inserter
template<class Container>
std::front_insert_iterator<Container> front_inserter( Container& c )
{
return std::front_insert_iterator<Container>(c);
}
// std::inserter
template<class Container>
std::insert_iterator<Container> inserter(Container& c, typename Container::iterator i)
{
return std::insert_iterator<Container>(c, i);
}
- inserter 계열의 클래스는 컨테이너에 새로운 원소를 삽입할 때 사용하는 특수한 출력 반복자 어댑터(Output Iterator Adapter)로, 3가지 종류가 있으며, 각각 아래와 같다.
- std::back_inserter
- 컨테이너 맨 뒤에 원소를 삽입한다.
- 내부에서 push_back(value)을 호출하기 때문에, push_back 멤버 함수가 존재하는 컨테이너만 사용가능하다.
- 사용 가능 컨테이너: std::vector, std::deque, std::list, std::string 등
- std::front_inserter
- 컨테이너 맨 앞에 원소를 삽입한다.
- 내부에서 push_front(value)를 호출하기 때문에, push_front 멤버 함수가 존재하는 컨테이너만 사용가능하다.
- 사용 가능 컨테이너: std::deque, std::list, std::forward_list 등
- std::inserter(일반 삽입자)
- 컨테이너의 특정 위치에 원소를 삽입한다.
- 내부에서 insert(it, value)를 호출하기 때문에, insert 멤버 함수가 존재하는 컨테이너만 사용가능하다.
- 사용 가능 컨테이너: 거의 모든 STL 컨테이너
- 특징: 연관 컨테이너(set, map)의 경우, 삽입 위치 인자인 it을 힌트로만 사용하며 실제로는 정렬된 위치에 삽입된다.
- std::back_inserter
조건 복사 std::copy_if
template <class InputIt, class OutputIt, class UnaryPredicate>
OutputIt copy_if(InputIt first, InputIt last, OutputIt d_first,
UnaryPredicate pred);
- 복사를 하되, 조건에 맞는 요소만 복사하고 싶다면 std::copy_if를 사용하면 된다. 4개의 인자를 전달해야 하며, 앞에 3개는 std::copy와 동일하게 넣은 다음 마지막 인자에 “단항 서술자(UnaryPredicate)”를 넣어주면 된다. 예제는 아래와 같다.
vector<int> v2;
std::copy_if(v1.begin(), v1.end(), back_inserter(v2), [](const int& elem){
// 짝수만 복사
return elem % 2 == 0;
});
for(const int& elem : v2)
{
cout << elem << ' ';
}
cout << '\\n';
// 실행 결과
2 4
단항 서술자(UnaryPredicate)?
- STL 알고리즘에 인자로 Predicate 타입이 자주 나오는데, C++에서 Predicate는 반환 타입이 bool인 함수로, Predicate 계열인 단항 서술자는 인자가 한 개인 Predicate를 의미한다. 즉, 단항 서술자는 “인자가 한 개이고, bool 타입을 반환하는 functor”를 의미한다.
'C++ > 문법 및 메소드(STL)' 카테고리의 다른 글
| [C++] 가끔 C 배열을 쓰고 싶을 때를 위한 글 (1) | 2026.01.12 |
|---|---|
| [C++][Etc] 메모리 재사용 시스템 때문에 댕글링 포인터가 메모리 오염 포인터가 되었던 건에 대해서 (0) | 2025.06.14 |
| [C++][Etc] 쓰레기 값에도 패턴이 있을까? (0) | 2025.06.14 |
| [C++][Build] 컴파일 단계 - 심볼, 저장 방식 지정자, 그리고 이름 맹글링 (0) | 2025.04.14 |
| [C++][Build] 컴파일 단계 - 번역 단위(translation unit)와 선언과 정의, 그리고 ODR (0) | 2025.04.14 |