본문 바로가기
C,C++/C++

[C++] std::transform() 함수 및 back_inserter 함수에 대해 알아보자

by woohyeon 2019. 11. 4.
반응형

안녕하세요.
오늘은 <algorithm> 헤더에 정의되어 있는 STL
 알고리즘인 std::transform() 함수에 대해 알아보겠습니다.


transform() 함수는 3개의 반복자와 1개의 단항 연산 함수를 매개변수로 갖습니다. 
( 바이너리 연산 함수를 인자로 갖기도 하지만 여기선 단항 연산 함수만 다루겠습니다. )

template< class InputIt, class OutputIt, class UnaryOperation >
OutputIt transform( InputIt first1,
                    InputIt last1,
                    OutputIt d_first,
                    UnaryOperation unary_op
                  );

: transform() 함수는 [first1, last1) 범위의 각 요소마다 함수를 호출하고 그 결과를 d_first 위치에 추가합니다.

first1, last1
 Input 컨테이너의 범위를 나타냅니다.

d_first
각 요소의 결과 값인 output이 추가될 위치를 나타냅니다.

unary_op
단항 연산 함수입니다.
Input 컨테이너의 각 요소들이 해당 함수를 거쳐 d_first 위치에 추가됩니다.

 


예제로 이해하는 것이 제일 빠르니 예제로 살펴보겠습니다!

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>  // std::transform()
#include <iterator>   // std::back_inserter()


using std::string;
using std::vector;
using std::cout;

// myvector의 모든 요소에 순차적으로 2를 곱연산하여 
// output 컨테이너의 끝에 순차적으로 추가합니다.

int main(void)
{
	
	
	// 1 2 3 4 5
    vector<int> myvector {1, 2, 3, 4, 5};

	// 10 10 10 10
	vector<int> output(4, 10);

	/*
		1. myvector의 [begin, end) 범위의 각 요소를 람다 함수의 인자 n에 전달합니다.
		2. 각 결과마다 output 
 		ex) myvector의 첫 요소인 1에 2를 곱하여 추가, 다음 요소인 2에 2를 곱하여 추가..
	*/
	std::transform(myvector.begin(), myvector.end(), std::back_inserter(output),
			[](int n) { return n*2; });

	// 10 10 10 10 2 4 6 8 10
	for (vector<int>::const_iterator iter = output.begin(); iter != output.end(); ++iter)
	{
		cout << *iter << " ";
	}

	cout << "\n";

	return 0;
}


결과)


설명)

위 프로그램은
myvector의 모든 요소에 순차적으로 2를 곱연산하여 output 컨테이너의 끝에 순차적으로 추가합니다.

transform() 함수의 동작을 살펴보면 먼저 처음 2개의 인수로 주어진 [ myvector.begin(), myvector.end() ) 범위가 유효한지 확인합니다.

다음으로 위 범위의 각 요소에 람다식을 적용하여 2를 곱연산 한 값을 3번째 인수인 std::back_inserter(output)이
반환하는 위치에 추가합니다. 반복자를 증가시켜 다음 요소에도 똑같은 동작을 반복합니다.

이 때 주의할 점은 transform() 함수가 결과 값을 추가할 저장 공간을 확보해주지 않습니다. 무슨 말이냐면 만약 vector의 capacity가 0일 때 push_back과 같은 함수가 아닌 단순히 인덱스를 사용하여 vec[0] = 3 과 같이 대입('=')을 한다면 유효하지 않은 공간에 접근하기 때문에 런타임 에러가 납니다.  아래 1312 라인을 보면 transform 함수가 요소를 추가할 때 push_back()과 같은 연산이 아닌 '=' 연산자를 사용하여 값을 대입합니다. 

trasform() 함수 정의


따라서 추가할 위치의 메모리 공간이 유효하지 않다면 런타임 에러를 발생시킵니다. 즉 추가하려는 위치의 공간이 미리 확보가 되어있어야 합니다. 혹은 push_back 함수처럼 자동으로 공간을 확보하며 추가할 수 있도록 해주어야 합니다.

이것을 가능하게 해주는 것이 바로 예제에서 사용한 back_insert_iterator 타입을 반환하는 back_inserter() 함수입니다.

back_inserter

back_inserter 함수는 컨테이너를 받아 back_insert_iterator 타입의 객체를 생성하여 반환합니다. back_insert_iterator 클래스는 이름 그대로 뒤에 삽입을 위한 일종의 반복자 클래스입니다. 이 클래스는 내부적으로 대입(=) 연산자 오버로딩이 되어있습니다. (copy assignment 아님) 

그 내용을 살펴보면 아래와 같이 '=' 연산을 통해 back_insert_iterator 타입의 변수에 값을 대입할 경우 push_back() 함수로 값을 추가해주고 있습니다. 즉 외부에서 '='을 통해 대입을 하면 내부적으로 push_back으로 변경하여 추가하기 때문에 공간 확보에 대한 신경을 써주지 않아도 된다는 뜻입니다.

back_insert_iterator 클래스

 

참고)
https://en.cppreference.com/w/cpp/algorithm/transform

https://docs.microsoft.com/ko-kr/cpp/standard-library/back-insert-iterator-class?view=vs-2019




댓글