관리 메뉴

공부 기록장 💻

[C++] 템플릿(Template) - 제네릭 함수(Generic Function), 제네릭 클래스의 구체화(Generic Class Specialization), 표준 템플릿 라이브러리(STL: Standard Template Library) 본문

# Language & Tools/C++

[C++] 템플릿(Template) - 제네릭 함수(Generic Function), 제네릭 클래스의 구체화(Generic Class Specialization), 표준 템플릿 라이브러리(STL: Standard Template Library)

dream_for 2021. 5. 20. 23:59

(명품 C++ 프로그래밍 Ch10)

 

 

 

함수 중복(function overloading) 은 편리하지만,

동일한 코드가 반복되어 중복된 함수를 여러번 정의하고 구현해내야 한다는 약점이 있다.

 

 

 

다음은 두 값을 서로 교환하는 myswap 함수이다.

int, double 이라는 매개 변수 타입만 다르지, 알고리즘이 동일하여 동일한 코드가 반복되어 작성되었다.

(swap 함수는 C++ 표준 템플릿 라이브러리에 이미 정의되어 있으므로, 이름 충돌을 피하기 위해 myswap()을 사용)

 

// 매개변수의 타입만 다른 중복된 두 myswap 함수

void myswap(int &a, int &b){
	int tmp;
	tmp=a;
	a=b;
	b=tmp;
}

void myswap(double &a, double &b){
	double tmp;
	tmp=a;
	a=b;
	b=tmp;
}

 

위의 myswap 함수와 같이

매개 변수 타입만 다른 중복된 함수들을 일반화시킨 틀(template)을 만들어,

매개 변수 타입만 지정하여 찍어내는 방법에 대해 알아보자.

 

 


 

일반화와 템플릿

 

템플릿(template): 함수나 클래스 코드를 찍어내듯 생산할 수 있도록 일반화(generic) 시키는 도구

 

앞에서 예시로 나온 myswap 함수를 템플릿 함수(template function) 또는 제네릭 함수(generic function)로 바꾸어보자.

(template 키워드를 사용해서 일반적인 함수(중복함수들)를 템플릿 함수로 바꾸는 것을 일반화 시키는 과정이라고 한다.)

 

template <class T> // 템플릿 선언과 제네릭 타입 T 선언
void myswap(T &a, T &b){
	T tmp;
	tmp=a;
	a=b;
	b=tmp;
} 

 

- template: 템플릿을 선원하는 키워드
- class: 제네릭 타입을 선언하는 키워드 (<typename T> 선언 가능. typename 키워드를 사용할 수 있다.)

- T: 제네릭 타입 T

     - 제네릭 타입(generic type)은 기본 타입을 일반화 시킨 새로운 타입이다. 개발자가 원하는 이름을 붙이면 된다.

 

 

template <class T1, class T2, class T3>
template <typename T1, typename T2, typename T3>

 

3개의 서로 다른 타입이 사용되는 중복 함수에 대해서는,

다음과 같이 T1, T2, T3 의 3개의 제네릭 타입을 선언할 수도 있다. 

 

 

 

템플릿 선언부는 다음과 같이, template 선언과 함수 선언을 한 줄에 붙여 써도 무방하다. 

 

template <class T> void my swap(T &a, T &b){
...
}

 


 

템플릿으로부터의 구체화(specialization)

 

- 구체화(specialization) : 중복 함수들을 템플릿화하는 과정의 역과정

- 구체화된 함수(specialized function): 컴파일러가 함수의 호출문을 컴파일하여, 구체화를 통해 제네릭 함수로부터 구체적인 함수의 소스 코드를 만들어내는데, 이때 구체화를 통해 생성되는 함수

 

int a=4, b=5;
myswap(a, b); // 제네릭 타입 T에 int를 대입하여 구체화시킨 함수를 생성하여 호출

 

위의 예제에서 int형인 a와 b를 myswap의 인수로 전달하면,

템플릿 함수 myswap(T &a, T &b) 는 컴파일로부터 구체화되어, myswap(int &a, int &b) 가 실행된다.

 

 

 

 

구체화하여 컴파일하는 과정의 단계는 다음과 같다.

 

1. 컴파일러는 myswap(a, b); 호출문을 컴파일 할 때, myswap() 함수를 탐색함

2. 템플릿으로 선언된 myswap() 함수 발견

3. 구체화: myswap(a, b); 의 함수 호출문에서 실인자 a,b 모두 int 타입이므로 템플릿의 제네릭 타입 T에 int를 대입시켜 구체화된 버전의 myswap(int &a, int &b) 의 소스코드를 만들어 낸다

4. 구체화된 함수의 소스 코드를 컴파일하고, 이 함수를 호출하도록 컴파일함

 


템플릿의 역할

템플릿 함수는 그저 함수의 '틀'이다. 제네릭 함수를 선언하고, 컴파일 시점에 구체화시키기 위한 '틀'인 것이다.

(객체를 생성하지않음)

 

 

구체화할 때 주의점

제네릭 타입 T에 유의해야 한다. 위 myswap() 함수의 두 실인자의 타입은 동일해야 한다.

 

 

템플릿의 장단점

- 소프트웨어의 생산성과 유연성을 높임 (함수 작성 용이, 함수 코드 재사용)

- 포팅에 취약(컴파일러에 따라 템플릿이 지원되지 않을 수도 있음)

- 템플릿과 관련된 컴파일 오류 메시지가 빈약 -> 디버깅의 어려움

 

 

제네릭 프로그래밍(generic programming) , 일반화 프로그래밍

- 템플릿을 이용해 제네릭 함수/클래스를 만들고 활용하여 프로그램을 작성하는 것

- C++, Java, C# 등 여러 언어에서 제네릭 프로그래밍을 지원함 -> 많은 라이브러리들이 제네릭으로 수정되어 있음

 

 

제네릭과 매크로의 차이점

C언어에서의 매크로 ( #define 이용하여 매크로 함수 작성)

- 복잡한 함수나 클래스를 표현하는데 한계 존재

 - 타입 안전성(type-safe) 이 확보되지 않아 실행 중에 부작용을 초래할 가능성이 높다.

템플릿: 제네릭 타입에 적용되는 실제 타입을 검사하여, '구체화 과정'을 거치기 때문에 타입 안전성이 확보된다.

 

 

 


제네릭 함수(Generic Function)

 

 

제네릭 타입 : 배열/포인터형

 

헷갈리는 부분일 수 있다!

배열을 제네릭 타입으로 선언 가능할까...?

배열은 타입은 아니지만, 어떠한 기본 타입의 '포인터형'이라고 할 수 있다.

(배열의 이름이 포인터와 같으므로)

 

따라서 배열, 또는 포인터로 명시하지 않아도 구체화하는 과정에서 자동적으로 포인터형으로 변환하여 컴파일하게 된다.

 

다음은 배열의 특정 인덱스 값에 어떠한 요소를 삽입(또는 기존 요소 변경)하는

insert 제네릭 함수이다.

 

#include <iostream>
using namespace std;

template <class T1, class T2>
void insert(T1 a, T2 b, int index){
	b[index]=a;
}

int main(){
	int x[3]={1,2,3};
	
	for(int i=0;i<3;i++) cout<<x[i]<<" ";
	cout<<endl;
	
	insert(3, x, 0);
	for(int i=0;i<3;i++) cout<<x[i]<<" ";
	cout<<endl;
}

 

위의 T2 타입의 b를 배열 [] 또는 포인터* 이라는 것을 명시하지 않아도,

자동으로 구체화되어 원본 배열이 변경된다.

 

 

 

두 개 이상의 제네릭 타입을 가진 경우

 

두 개의 배열과 배열의 크기를 받아 배열을 복사하여 저장하는 제네릭 mcopy() 함수에 대한 예제이다.

두 배열의 타입이 다를 수 있으므로, 템플릿의 제네릭 타입에 두 개의 서로 다른 타입(T1, T2)을 선언하였다.

 

#include <iostream>
using namespace std;

template <class T1, class T2> // T1, T2 두 제네릭 타입 선언
void mcopy(T1 src[], T2 dest[], int n){
	for (int i=0;i<n;i++)
		dest[i]=(T2)src[i]; // 배열 요소들을 복사할 때는 형변환이 필요함
}
int main(){
	int x[5]={1,2,3,4,5};
	double y[5];
	char z[5]={'h', 'e', 'l', 'l', 'o'}, w[5];
	
	mcopy(x,y,5);
	mcopy(z,w,5);
	
	for(int i=0;i<5;i++) cout << y[i] << " "; // T1은 int로, T2는 double로 구체화됨
	cout<<endl;
	for (int i=0;i<5;i++) cout << w[i] <<" "; // T1, T2 모두 char로 구체화됨	
	cout<<endl;
}

 

서로 다른 타입에 대해 배열을 복사하는 것이므로, 배열 요소들 하나하나를 복사할 때 형변환을 꼭 해주어야 한다.

구체화될 때, 제네릭 타입인 T1과 T2는 main 함수에서 호출될 때 실인자의 타입으로 각각 구체화되어 컴파일된다. 

 

 

 

 

 

중복 함수가 템플릿 함수보다 우선

 

다음과 같이 char 문자형 배열인 c와 n이 있다.

문자를 요소로 갖는 c와 다르게 n은 int형 배열 요소들을 갖고 있다.

이 때 각 배열을 템플릿 함수로 선언된 print 로 호출하여 구체화과정을 거쳐 출력해보자.

 

#include <iostream>
using namespace std;


template <class T>
void print(T arr[], int n){
	for (int i=0;i<n;i++)
		cout<<arr[i]<<" ";
	cout<<endl;
}

int main(){
	char c[5]={'h', 'e', 'l', 'l', 'o'};
	char n[5]={1,2,3,4,5}; // int n[5]={} 무방
	print(c,5);
	print(n,5);
}

 

아래와 같이 c 배열은 올바르게 요소들이 출력됐지만,

n과 같은 경우는 정수값에 해당하는 아스키 문자가 없으므로, 윈도우 운영체제의 문자 코드표에 의해 정의된 그래픽 문자들을 출력한다.

 

 

 

char 문자형 배열의 정수 요소들을 문자 형태 그대로 출력하기 위해서는 다음과 같이,

템플릿 함수와 동일한 print 함수를 다르게 작성하여 중복 정의해주면 된다.

 

이 때, 컴파일러는 중복된 함수를 템플릿 함수보다 우선하여 바인딩한다.

 

void print(char a[], int n){
	for (int i=0;i<n;i++)
		cout<<(int)a[i]<<" ";
	cout<<endl;
}

 

따라서 위의 코드를 추가하여 실행시키면,

다음과 같이 기존 c 배열에 대해서도 중복 함수가 우선 실행되어

문자 요소들의 아스키코드 정수값을 출력하고,

n 배열에 대해서도 기존 int형 정수 요소들을 그대로 올바르게 출력했음을 확인할 수 있다.

 

 

h, e, l, o 각 문자에 해당하는 아스키코드값 확인

 

 

 

템플릿 함수에 디폴트 매개 변수 사용하기

 

템플릿 함수의 매개 변수에도 디폴트값을 설정할 수 있다.

다음과 같이 배열을 복사하는 mcopy 함수의 마지막 n 매개변수의 디폴트 값을 5로 지정했다.

함수를 호출할 때, 세번째 실인자값을 주지 않으면 자동으로 5가 n값으로 지정되어 함수가 실행된다.

 

template <class T1, class T2> 
void mcopy(T1 src[], T2 dest[], int n=5){ // n의 디폴트값 5
	for (int i=0;i<n;i++)
		dest[i]=(T2)src[i]; 
}

 

 

 


 

제네릭 클래스(Generic Class)

 

template 을 이용해 제네릭 클래스(generic class)를 만들어보자.

특히 스택(stack)의 경우 int, double, char, 객체 등 여러가지 다양한 자료를 넣을 수 있는 구조인데

스택 클래스의 구조는 어떤 자료를 저장하느냐에 따라 타입만 다를 뿐이지, 그 알고리즘은 모두 동일하다.

딸서 템플릿을 이용해 데이터 타입을 일반화 시킨 제네릭 스택 클래스를 만들어보자!

 

 

 

제네릭 클래스 선언부

 

기존과 동일하게, template 키워드 그리고 제네릭 class 키워드로 제네릭 타입 T에 대해 선언한다.

선언한 제네릭 타입 T를 이용해,

다음과 같이 MyStack 스택 클래스 선언부를 작성한다.

 

// 제네릭 클래스 구현부

template <class T>
class MyStack{
	int top; 
	T data[100]; // 100개의 요소를 저장 가능 
public:
	MyStack(); // 생성자 함수
	void push(T element); // 요소를 삽입 
	T pop(); // 요소를 반환하고 삭제 
};

 


 

제네릭 클래스 구현부

 

template <class T>
MyStack<T>::MyStack(){...}

template <class T>
void MyStack<T>::push(T element){...}

template <class T>
T MyStack<T>::pop(){...}

 

제네릭 클래스의 구현부에는,

클래스이름과 함께 <제네릭 타입>을 적어주어야 한다.

각 멤버 함수 앞에는 템플릿 정의 부분 template<class T> 을 붙여 제네릭 함수를 선언하게 된다.

 

 

함수의 내용을 추가하여 구현부를 작성하면 다음과 같다.

 

// 제네릭 클래스 구현부  

template <class T>
MyStack<T>::MyStack(){
	top=-1;
}

template <class T>
void MyStack<T>::push(T element){
	if(top==99){
		cout<<"stack full"<<endl;
		return;
	}
	data[++top]=element;
}

template <class T>
T MyStack<T>::pop(){
	if(top==-1){
		cout<<"stack empty"<<endl;
		return 0;
	}
	return data[top--];
}

 

 


 

제네릭 클래스의 구체화

 

제네릭 클래스 객채 생성

클래스 이름과 <구체적인 타입> 을 지정하여 객체를 생성한다.

MyStack<int> iStack; // int타입 다루는 MyStack 클래스의 객체 iStack
MyStack<char> cStack; // char타입 다루는 MyStack 클래스의 객체 cStack

 

컴파일러의 구체화 과정은 다음과 같다.

 

1. MyStack 템플릿의 T에 구체적인 타입(int, char)을 적용하여 구체화된 버전의 클래스 소스(specialized class)를 생성

2. 두 클래스를 컴파일하고 두 객체를 생성하도록 컴파일함

 

 


 

스택에 대한 제네릭 클래스의 구현부, 선언부에 대하여

다음과 같은 main 함수를 선언해보자.

 

int main(){
	MyStack<int> iStack;
	for (int i=1;i<=10;i++)iStack.push(i); // 1부터 10까지 push
	while(int n=iStack.pop()) // 10부터 다시 pop하고 삭제
		cout<<n<<" ";
	cout<<endl;
	
	MyStack<char> cStack;
	for (char c='a';c<='z';c++) cStack.push(c); // 'a'부터 'z'까지 push
	while(char ch=cStack.pop()) // 'z'부터 다시 pop하고 삭제
		cout<<ch<<" ";
	cout<<endl;
}

 

 

 

 


 

다음과 같이 클래스의 객체 포인터를 생성하여 사용할 수도 있다.

 

MyStack<char> *p= new MysTack<char>();
p->push('a');
char c=p->pop();
delete p;

 

 

 

 

함수의 매개변수 타입이 제네릭 클래스인 경우(인자를 객체로 전달하게 되는 경우),

참조 매개변수를 사용하여 객체 복사의 오버헤드를 제거할 수 있다.

또는 포인터로 선언 가능하다.

 


 

C++ 표준 템플릿 라이브러리(STL: Standard Template Library)

- 표준 템플릿 라이브러리(STL) : 템플릿으로 작성된 많은 제네릭 클래스와 함수의 라이브러리 (C++ 표준)

- STL은 std 이름 공간 에 작성되어 있다. STL을 사용하기 위해서는 using namespace std;  를 추가해주어야 한다.

 

 

 

1. STL 컨테이너(container) - 템플릿 클래스

- 데이터를 저장하고 검색하기 위해 담아두는 '자료 구조'를 구현한 클래스 

- 스스로 크기를 조절하여 원소의 개수로 인한 사용자의 부담을 줄여준다.

 

 

컨테이너 클래스는 컨테이너에 저장되는 원소를 다루는 방식에 따라 다음의 3가지로 분류된다.

1. 순차 컨테이너(Sequence Container) - vector, dequeue, list 등 연속적인 메모리 공간에 순서대로 값을 저장하고 읽는 컨테이너(인덱스 사용)

2. 컨테이너 어댑터(Container Adaptor) - stack, queue과 같이 다른 컨테이너를 상속받아 기능 중 일부만 공개하여 기능을 제한하거나 변형한 컨테이너

3. 연관 컨테이너(Associative Container) - set, map과 같이  'key'로 'value'를 저장하고 'key'로 검삭해여 'value'를 알아내는 컨테이너

 

 

컨테이너 클래스 / <헤더 파일> 설명
vector / <vector> 가변 크기의 배열 
deque / <deque> 앞뒤 모두 입력 가능
list / <list> 빠른 삽입/삭제 가능한 리스트
set / <set> 정렬된 순서로 값을 저장하는 집합 클래스, 값은 유일
map / <map> (key, value) 쌍을 저장
stack / <stack> 스택
queue / <queue>

 

 


 

< STL vector 의 멤버 함수와 연산자 함수 >

 

멤버, 연산자 함수 설명
push_back(element) 벡터의 마지막에 element 추가
at(int index) index 위치의 원소에 대한 참조 리턴
begin() 벡터의 첫번째 원소에 대한 참조 리턴
end() 벡터가 끝(마지막 원소 다음)을 가리키는 참조 리턴
empty() 벡터가 비어있으면 true 리턴
erase(iterator it) 벡터에서 it이 가리키는 원소 삭제, 삭제 후 자동적으로 벡터 조절
insert(iterator it, element) 백터 내 it위치에 element 삽입
size() 벡터에 들어 있는 원소의 개수 리턴
operator[]() 지정된 원소에 대한 참조 리턴
operator=() 이 벡터를 다른 벡터에 치환(복사)

 

1. vector 객체 생성

2. 원소 삽입

3. 원소 값 읽기/변경

4. 원소 개수

5. 원소 삭제

 

 

정수 벡터 사용 예제

 

#include <iostream>
#include <vector>
using namespace std;

int main(){
	vector<int> v; // 정수 백터 생성
	
	// 정수 삽입 
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	
	cout<<"v.size() = "<<v.size()<<endl; // 원소의 개수 
	cout<<"v.capacity() = "<<v.capacity()<<endl<<endl;  // 벡터의 용량
	
	// at(), [] 을 이용한 원소 값 접근 
	for(int i=0;i<v.size();i++){
		cout<<"v.at("<<i<<") = "<<v.at(i)<<endl;
		v[i]*=2;
		cout<<"v["<<i<<"] = "<<v[i]<<endl<<endl;
	}	 
}

 

 

 

< STL map 의 멤버 함수와 연산자 함수 >

 

- 생성/원소 삽입

- '키'로 '값' 알아내기

멤버, 연산자 함수 설명
insert(pair<> &element) 맵에서 '키'와 '값'으로 구성된 pair 객체 element 삽입
(make_pair 함수 이용)
at(key_type& key) 맵에서 '키'값에 해당하는 '값' 리턴
begin() 맵의 첫번째 원소에 대한 참조 리턴
end() 맵의 끝(마지막 원소 다음)을 가리키는 참조 리턴
empty() 맵 비어있으면 true 리턴
erase(iterator it) 맵에서 it이 가리키는 원소 삭제
find(key_type& key) 맵에서 '키' 값에 해당하는 원소를 가리키는 iterator 리턴
size() 맵에 들어 있는 원소의 개수 리턴
operator[key_type &key]() 맵에서 '키'값에 해당하는 원소를 찾아 '값' 리턴
operator=() 맵 치환(복사)

 

 

 

map 컨테이너를 사용한 간단한 영-한 사전 프로그램 예제

 

#include <iostream>
#include <map>
using namespace std;

int main(){
	cout<<"map 컨테이너를 이용한 영한 사전 프로그램입니다."<<endl<<endl; 
	
	map<string, string> dic; // map 컨테이너 생성(키와 값 모두 string 문자열 객체)
	
	// 원소 저장 
	// insert() 멤버 함수 이용 - (love, 사랑) 쌍을 만들어 dic에 insert함 / [] 연산자 이용 
	dic.insert(make_pair("love", "사랑"));
	dic["apple"]="사과";

	string eng, kor;
	while(true){
		cout<<"사전에 추가하고 싶은 영어-한글을 입력하시오 (종료는 exit) >>"<<endl;
		getline(cin, eng);
		if(eng=="exit") break;
		getline(cin, kor);
		dic.insert(make_pair(eng, kor));
		cout<<"사전에 "<<eng<<" "<<kor<<"가 성공적으로 추가되었습니다"<<endl<<endl;
	}
	
	cout<<endl<<"사전에 저장된 단어 개수 : "<<dic.size()<<endl<<endl;
	cin.clear(); // 입력 버퍼 비우기 
	
	string get;
	
	while(true){
	 	cout<<"찾고 싶은 단어 (중료는 exit) >> ";
	 	getline(cin, get);
	 	if(get=="exit") break;
	
		// map에 key의 데이터가 있는지 검사하기 위해서는 end() 함수 이용
		if(dic.find(get) == dic.end())
		 	cout<<"dic에서 ["<<get<< "] 발견 못함"<<endl;
		else
			cout<<"dic["<<get<<"] = "<<dic[get]<<endl<<endl; 
	}
} 

 


 

 

 

 

2. STL iterator - 컨테이너 원소에 대한 포인터

- iterator (반복자) : 컨테이너의 원소들을 하나씩 순차적으로 접근하기 위한 컨테이너 원소에 대한 포인터

- 컨테이너 종류에 무관하게 컨테이너의 원소들을 검색할 수 있다.

- 원소를 읽고, 기록하는 iterator이 있다.

 

iterator 종류 iterator에 ++ 연산 후 방향 read/write
iterator 다음 원소로 전진 read/write
const_iterator 다음 원소로 전진 read
reverse_iterator 지난 원소로 후진 read/write
const_reverse_iterator 지난 원소로 후진 read

 

 

iterator 포인터 선언은 다음과 같이 할 수 있다.

 

vector<int> v; // 정수형 벡터 v 선언
vector<int>::iterator it; // 벡터 v의 원소에 대한 포인터 it 선언

 

 

 

벡터에 대한 iterator 포인터 사용 예제

 

#include <iostream>
#include <vector>
using namespace std;

int main(){
	vector<int> v; // 정수 백터 생성
	vector<int>::iterator it; // 벡터 v의 원소에 대한 포인터 it 선언
	 
	// 정수 삽입
	for(int i=0;i<10;i++){
		v.push_back(i);
		cout<<v[i]<< ' ';
	}
	cout<<endl<<endl;
	
	cout<<"v.size() = "<<v.size()<<endl; // 원소의 개수 
	cout<<"v.capacity() = "<<v.capacity()<<endl<<endl;  // 벡터의 용량
	
	// 포인터를 이용한 원소 값 접근
	for(it=v.begin(); it!=v.end(); it++){
		int n=*it;
		n*=2;
		*it=n; // 2배 곱한 값으로 갱신 
	} 
	
	for(it=v.begin(); it!=v.end(); it++)
		cout<<*it<<' ';
	cout<<endl;
}

 

 

 


3. STL 알고리즘 함수 

- #include <algorithm>

- 컨테이너 클래스의 멤버 함수가 아닌, 템플릿으로 작성된 전역 함수

- 종류: copy, merge, random, rotate, equal, min, remove, search, find, move, replace, sort, max, partition, reverse, swap

 

 

STL 알고리즘 함수는 iterator 과 함께 작동한다.

 

 

sort() 알고리즘 함수를 이용하여 값들을 오름차순으로 정렬하는 예제를 살펴보자.

 

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(){
	vector<int> v;
	vector<int>::iterator it;
	int n, tmp;
	
	cout<<"정수 벡터 몇 개? >> ";
	cin>>n;
	cout<<n<<"개의 정수 입력 >> ";
	for(int i=0;i<n;i++){
		cin>>tmp;
		v.push_back(tmp);
	}
	
	// v.begin() 첫번째 원소와 v.end() 마지막 원소 사이의 값을 오름차순으로 정렬 
	sort(v.begin(), v.end());
	
	cout<<"정렬 후 >> ";
	for(it=v.begin(); it!=v.end(); it++)
		cout<<*it<<' ';
	cout<<endl;
}

 

 

 

 


auto와 람다식

 

auto 키워드: 변수 선언문으로부터 변수의 타입을 추론하여 결정하도록 지시

- 복잡한 형식의 변수 선언을 간소하게 해주며, 타입의 선언의 오타나 번거로움을 줄인다

 

auto pi = 3.14; // pi가 double 타입으로 선언됨
auto *p = pi; // double* 타입으로 자동 선언

double &ref = pi;
auto ref2 = ref; // 참조 변수 선언- double& 타입으로 자동 선언

 

 

 

함수의 리턴 타입으로부터 추론하여 변수 타입 선언

- 코드의 수정에 따른 오류 가능성을 낮출 수 있다.

 

int squre(int x) { return x*x;}
...
auto result = square(3); // int 타입으로 자동 선언

 

 

STL 템플릿에 활용

- vector, map 등의 템플릿에 사용하여 복잡한 변수 선언을 간소화할 수 있다.

 

다음 예제에서는 int형으로 선언된 v 벡터에 대한 iterator 포인터에 대한 선언을 간소화할 수 있다.

vector<int>::iterator it; 처럼 복잡한 모양의 선언이 없어도, auto를 사용하면 다음과 같이 간략히 작성할 수 있다.

 

vector<int> v = {1,2,3,4,5};

for (auto it = v.begin(); it!=v.end(); it++) // vector<int>::iterator it 
	cout<< *it << endl; // 추론 가능한 문장

 


람다식(lambda expression) , 람다 함수 : 이름 없는 익명 함수(anonymous function)

 

[ 캡쳐 리스트 ] ( 매개 변수 리스트 ) - > 리턴타입 { /* 함수 코드 작성 */ }

  • 캡쳐 리스트
    • [x] - 변수의 x값 활용
    • [&x] - 참조 변수 x 활용
    • [=] 모든 변수의 값 활용
    • [&] - 모든 참조 변수 활용
  • 매개 변수 리스트
  • 함수 바디 - 람다식 호출될 때 실행되는 코드

 

[](int x, int y) { cout<<x+y; }; // 매개 변수 x와 y의 합을 출력하는 람다
[](int x, int y) -> int {return x+y; }; // 매개변수 x,y의 합을 리턴하는 람다
[](int x, int y) { cout << x+y; } (2,3); // x에 2, y의 3 대입하여 코드 실행

 

728x90
반응형
Comments