본문 바로가기
Programming/C++

C++ 언어 기초 (15) - STL, vector, 알고리즘, map

by 롱일스 2020. 9. 7.
반응형

▣ 표준 템플릿 라이브러리(STL)

 ●  Standard Template Library, STL

  ▷ STL이란?

  •   C++이 제공하는 표준 컨테이너 클래스 템플릿 라이브러리
  •   vector, list, stack, queue 등의 Container와 이들을 처리하기 위한 여러 연산을 포함하고 있다.

  ▷ STL의 구성 요소

  • 컨테이너 container : 데이터 저장 기능
  • 반복자 iterator : 포인터 역할
  • 알고리즘 algorithm : 데이터 처리 기능

 

 ●  STL container 컨테이너

  ▷ 데이터 저장을 위한 template의 집합이다.

  • int나 float과 같은 기본 자료형 데이터나 사용자 정의 클래스의 객체 등을 저장한다.
  • 다양한 연산이 제공되어서 편리하게 데이터를 활용할 수 있다.
  • ++) 배열이 일종의 컨테이너의 역할을 하지만 배열의 경우에는 조작하기 위한 연산을 프로그래머가 모두 구현해야 한다. 여기서 연산에는 특정 위치에 데이터를 삽입, 데이터를 삭제, 데이터를 검색 하는 연산을 포함한다.

 

 ●  STL container 종류

  ▷ 순차 컨테이너 sequential container

  • 동일한 자료형의 객체들을 선형적인 구조로 저장한다.
종류 특성
vector - 크기의 확장이 가능한 배열이라고 볼 수 있다.
- [ ] 연산자로 지정한 첨자를 이용하여 빠른 직접 접근한다.
- 크기 확장이 가능해서 끝에 삽입/삭제 하는 것은 빠르지만 그 외의 위치에 삽입/삭제 하는 것은 느리다. (삽입/삭제하려는 위치 위에 있는 데이터를 옮겨야 해서)
list - 이중 연결 리스트 double linked list 형태의 구조다.
- 어느 위치에는 삽입/삭제가 효율적이다.
- 직접 접근이 비효율적이라 제공하지 않는다. (왜냐하면 처음 위치부터 원하는 위치까지 순차적으로 데이터를 봐야하기 때문이다.)
deque - vector와 list의 혼합 형태다.
- vector와 list의 특성이 모두 필요할 때 사용할 수 있지만 성능은 낮다.

 

  ▷ 연상 컨테이너 associative container

  • 탐색 트리와 같은 인덱스 구조를 이용하는 컨테이너다.
  • 키를 이용한 효율적인 검색 기능을 제공한다.
종류 특성
set - (key 객체)만 저장하고, key가 중복되지 않는다.
multiset - (key 객체)만 저장하고, 동일한 key가 중복될 수도 있다.
map - (key 객체, value 객체)의 쌍을 저장한다.
- key가 중복되지 않는다.
multimap - (key 객체, value 객체)의 쌍을 저장한다.
- 동일한 key가 중복될 수 있다.

 

  ▷ 무순서 연상 컨테이너 unordered associative container

  • 연상 컨테이너처럼 key를 이용한 검색기능을 제공한다.
  • 해시함수를 이용해서 데이터 검색 시간이 일정하다.
종류 특성
unordered_set - set, multiset, map, multimap과 같지만 해시함수를 이용해서 저장과 검색을 한다는 것에서 다르다.
unordered_multiset
unordered_map
unordered_multimap

인덱스를 이용해서 데이터를 찾는 방식은 데이터의 크기가 커지면 인덱스도 많아져야 하기 때문에 비효율적일 수 있다. 해시함수를 이용하면 데이터의 양에 관계 없이 데이터 검색 시간이 일정해진다. key의 순서대로 데이터를 검색하는 것 같은 일은 하기 어렵다. 그래서 무순서 연상 컨테이너라고 한다.

 

  ▷ 컨테이너 어뎁터 container adaptor

  • 기본 컨테이너를 기반으로 특정 용도에 맞게 고안된 컨테이너다.
종류 특성
queue - FIFO(First In, First Out) 구조
priority_queue - 우선순위에 따라 데이터를 액세스할 수 있는 구조
stack - LIFO(Last In, First Out) 구조

 

 

 ●  반복자 iterator

  ▷ iterator가 뭔가?

  • 컨테이너 안의 데이터를 가리키는 포인터의 역할을 한다.
  • 포인터의 개념이 일반화된 것이다.
  • 컨테이너의 유형에 따라 서로 다른 형태의 iterator가 사용된다.
종류 특성
순방향(forward) iterator - 컨테이너의 순방향으로만 움직일 수 있다.
- (++) 연산자를 사용한다.
양방향(bidirectional) iterator - 컨테이너의 순방향과 역방향으로 움직일 수 있다.
- (++), (--) 연산자를 사용한다.
Random Access iterator - 양방향 iterator의 기능과 함께 임의의 위치로 이동할 수 있다.

 

 ●  알고리즘 Algorithm

  ▷ 컨테이너의 원소에 대해 적용 가능한 여러 연산

알고리즘 용도
search - 지정된 값과 동일한 첫 번째 원소 반환
count - 지정된 값을 갖는 원소의 수 반환
swap - container 내의 값을 교환
sort - container의 값들을 지정된 순서에 따라 정렬
merge - 두 정렬된 영역의 원소들을 합병
reverse - 지정된 범위의 원소들을 역순으로 나열
remove - container에서 지정된 값을 제거
replace - 지정된 값을 다른 값으로 대체
unique - 인접 위치에 있는 중복된 값을 제거
for_each - 지정된 함수를 container의 모든 원소에 적용

 

 

▣ STL 활용 예 - vector

 ●  vector

  ▷ vector가 뭔가?

  • 1차원 배열의 개념을 구현한 순차 컨테이너 유형의 class template
  • 배열의 일반적인 기능을 가지면서 여러 유용한 멤버함수 및 관리 기능도 갖고 있다.
  • 배열처럼 크기가 고정돼있지 않고 필요에 따라 저장 공간 확장 가능하다.
  • #include 명령으로 헤더파일 <vector>를 프로그램에 삽입하면 된다.

 

  ▷ vector 객체 선언 구문

vector<ClassName> objName(n);
// n: 벡터에 저장할 객체의 수
  • vector 객체 선언 예시 - 10개의 int 값을 저장하는 vector 선언
vector<int> iVector(10);

 

  ▷ [ ] 연산자

  • vector에 대한 직접접근 연산자로 배열처럼 첨자를 지정해서 원소를 직접접근하게 한다.
  • [ ] 연산자 사용 예시
vector<int> iVector(10);
iVector[2] = 5;
cout << iVector[2];

 

  ▷ 멤버함수 size( )와 capacity( )

  • vector의 크기는 실행 중에도 확장할 수 있다.
  • 미래의 확장에 대비하여 여분의 공간을 미리 확보할 수 있다.
  • 논리적인 vector 크기와 실제 할당된 메모리 크기가 다를 수 있다.

  • vector의 확장을 위해 size밖의 capacity 공간까지 확장할 수 있다.

 

  ▷ 멤버함수 push_back( )과 pop_back( )

  • push_back( ) : vector의 에 데이터를 저장
  • pop_back( ) : vector의 의 데이터를 꺼낸다.
  • 이 동작들에 의해 vector의 논리적 크기(size)가 바뀐다. (물론 컴파일러마다 그 정도가 다름)

 

  ▷ 멤버함수 insert( )과 erase( )

  • insert( ) : vector의 지정된 위치에 데이터를 삽입한다.
  • erase( ) : vector의 지정된 위치의 데이터를 삭제한다.
  • 함수의 실행에 따라 논리적인 데이터의 크기인 size( )의 값이 증가/감소 한다.
  • capacity( )의 값은 데이터 추가로 인해 확보된 메모리가 부족해서 확장할 때 바뀐다.

 

 ●  vector 사용의 예시

  ▷Vector1.cpp

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

int main()
{
    vector<int> intVec(5);
    
    for (int i = 0; i < intVec.size(); i++)
        intVec[i] = i + 1;
    
    cout << "vector의 논리적 크기 : " << intVec.size() << endl;
    cout << "vector의 물리적 크기 : " << intVec.capacity() << endl;
    cout << "vector에 저장된 데이터 : ";
    for (int i = 0; i < intVec.size(); i++)
        cout << intVec[i] << " ";
    
    cout << endl << endl << "1개 데이터를 push_back" << endl;
    intVec.push_back(11);   // intVec[5] = 11; size +1 up 
    cout << "vector의 논리적 크기 : " << intVec.size() << endl;
    cout << "vector의 물리적 크기 : " << intVec.capacity() << endl;
    cout << "vector에 저장된 데이터 : ";
    for (int i = 0; i < intVec.size(); i++)
        cout << intVec[i] << " "; 
    
    cout << endl << endl << "5개 데이터를 push_back" << endl;
     for (int i = 1; i <= 5; i++)
        intVec.push_back(i + 11);
    cout << "vector의 논리적 크기 : " << intVec.size() << endl;
    cout << "vector의 물리적 크기 : " << intVec.capacity() << endl;
    cout << "vector에 저장된 데이터 : ";
    for (int i = 0; i < intVec.size(); i++)
        cout << intVec[i] << " "; 
        
    cout << endl << endl << "3개 데이터를 pop_back" << endl;
     for (int i = 0; i < 3; i++)
        intVec.pop_back();
    cout << "vector의 논리적 크기 : " << intVec.size() << endl;
    cout << "vector의 물리적 크기 : " << intVec.capacity() << endl;
    cout << "vector에 저장된 데이터 : ";
    for (int i = 0; i < intVec.size(); i++)
        cout << intVec[i] << " "; 
    cout << endl;
    
    return 0;
}

 

  ▷ Vector1.cpp 출력결과

  • C++14로 컴파일러를 이용한 경우에 물리적 크기인 capacity가 논리적 크기에 비해 어느정도 더 여유있게 배당되었는지 볼 수 있다. (컴파일러마다 다르다) 
  • 물리적 크기를 보고 얼마나 더 vector를 확장할 수 있는지에 주목하라.
  • 데이터 삽입, 삭제의 구현이 가능하다.

 

 

 ●  vector의 크기 확장 및 데이터 조작 함수

조작 함수 용도
push_back(value) - 끝에 데이터 추가
pop_back( ) - 끝에 있는 데이터 제거
resize(n) - 논리적 크기 변경
reserve(n) - capacity( )가 최소한 n을 반환하도록 확장
empty( ) - 비어 있는 벡터의 경우 true 반환
erase(it) - 반복자 it가 가리키는 위치 삭제
erase(it1, it2) - [it1, it2) 범위의 데이터 삭제 // it1 <=  i < it2
insert(it, value) - 반복자 it가 가리키는 위치에 value 삽입

 

 

 ●  vector와 반복자

  ▷반복자 선언

vector<ClassName>::iterator it;

  ▷반복자의 값을 구하는 vector의 멤버함수

  • begin( ) : 첫 번째 원소를 가리키는 random access 반복자를 반환 (포인터 위치)
  • end( ) : 마지막 원소의 다음 위치를 가리키는 random access 반복자를 반환

 

 ●  vector의 반복자 활용 예시 

  ▷ Vector2.cpp

  • random access 반복자 이용해서 지정된 위치의 데이터 읽기가 가능하다.
  • 반복자를 이용해서 vector에 저장된 데이터에 포인터와 같이 접근할 수 있고, 직접접근이 가능하다.
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> intVec(5);
    
    for (int i = 0; i < intVec.size(); i++)
        intVec[i] = i + 1;
    
    // 1번 방법
    //vector<int>::iterator it = intVec.begin();
    // 2번 방법
    auto it = intVec.begin();   // 자료형 추론(auto)을 이용한 반복자 선언
    
    cout << "vector에 저장된 데이터 : ";
    for (; it < intVec.end(); it++)  //초기화 생략
        cout << *it << " ";
    cout << endl;
    
    it = intVec.begin();
    cout << "3번째 데이터 : ";  //random access 반복자 가능
    cout << *(it + 2) << endl;
    
    return 0;
}

 

  ▷ Vector2.cpp 출력결과

 

 

▣ 알고리즘의 활용

 ●  sort( ) 함수

  ▷ sort( )의 용법

  • random access 반복자로 지정된 범위의 값들을 순서대로 정렬한다.
//(1)번 유형
sort(first, last);
//(2)번 유형
sort(first, last, comp);

//first: 정렬할 범위의 시작 원소에 대한 포인터
//last: 정렬할 범위의 마지막 원소의 다음 위치에 대한 포인터
//comp: 정렬 순서를 정하는 함수 (callback 함수)
// a의 순서가 b보다 앞인 경우 comp(a,b) == true
  • first: 정렬할 범위의 시작 원소에 대한 포인터
  • last: 정렬할 범위의 마지막 원소의 다음 위치에 대한 포인터
  • comp: 정렬 순서를 정하는 함수 (callback 함수)
  • a의 순서가 b보다 앞인 경우 comp(a,b) == true
  • 나중에 사용할 수 있게끔 포인터를 전달하는 함수 - callback 함수

 

 ●  merge( ) 함수

  ▷ merge( )의 용법

  • 동일한 기준으로 정렬된 두 개의 데이터 집합을 동일한 기준으로 정렬된 하나의 데이터 집합으로 결합하는 함수다.
// (1) 유형
merge(first1, last1, first2, last2, dest);
// (2) 유형
merge(first1, last1, first2, last2, dest, comp);

// first1, last1 : 첫 번째 정렬된 데이터 범위
// first2, last2 : 두 번째 정렬된 데이터 범위
// dest : 합병 결과가 저장될 시작 위치
// comp : 합병 순서를 정하는 함수
  • first1, last1 : 첫 번째 정렬된 데이터 범위
  • first2, last2 : 두 번째 정렬된 데이터 범위
  • dest : 합병 결과가 저장될 시작 위치
  • comp : 합병 순서를 정하는 함수

 

 ●  알고리즘의 활용 예시

  ▷ Vector3.cpp

  • 난수 생성을 하기 위해 srand( ) 함수를 이용하는데, 이 함수를 이용하기 위해서는 <cstdlib>를 #include 해야 한다.
  • srand( )에는 인수로 초깃값을 줘야 하는데, 그 초깃값을 현재 시간에 기반해서 랜덤하게 주기 위해 time( ) 함수를 사용하고 이를 위해 <ctime>라이브러리를 포함한다.
  • iv1의 데이터를 바꾸기 위해 auto &i를 통해 반복자를 참조로 받는다.
  • sort 함수로 iv1.begin부터 iv1.end까지 정렬하라 했을 때 iv1.end는 데이터가 없는 공간을 가리키므로 그 전 데이터가 있는 벡터까지만 정렬할 것이다.
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <algorithm>
using namespace std;

int main()
{
    srand((unsigned)time(NULL)); // random number
    
    // vector 1
    vector<int> iv1(5);
    cout << "vector1 : ";
    for (auto &i : iv1){  //auto로 i의 자료형을 정수형으로 추론할 것이다.
        i = rand() % 100;    // 0 ~ 99
        cout << i << " ";
    }
    
    sort(iv1.begin(), iv1.end());  // 정렬 알고리즘
    cout << endl << "정렬된 vector1 : ";
    for (auto i : iv1)
        cout << i << " ";
    cout << endl << endl;
    
    vector<int> iv2(5);
    cout << "vector2 : ";
    for (auto &i : iv2){
        i = rand() % 100;    // 0 ~ 99
        cout << i << " ";
    }
    
    //vector 2 
    sort(iv2.begin(), iv2.end());
    cout << endl << "정렬된 vector2 : ";
    for (auto i : iv2)
        cout << i << " ";
    cout << endl << endl;
    
    //vector 3  <- 합병 결과 저장할 벡터 
    vector<int> iv3(iv1.size() + iv2.size());
    // vector1 + vector2 = vector 3
    merge(iv1.begin(), iv1.end(), iv2.begin(), iv2.end(), iv3.begin()); 
    cout << "vecter1 + vector 2 merge 결과 : ";
    for (auto i : iv3)
        cout << i << " ";
    cout << endl << endl;

    return 0;
}

 

  ▷ Vector3.cpp 출력결과

 

 ●  정렬 순서 결정

  ▷ 정렬 순서 지정을 위한 callback 함수 전달

  • sort(first, last, comp);
template<typename T>
bool greaterth(const T& a, const T& b) {
    return a > b;
}

void f(vector<int>& iv) {
    // 내림차순 정렬
    sort(iv.begin(), iv.end(), gt<int>);
}

데이터 양이 많을 땐 gt<int>로 함수가 반복적으로 호출되기 때문에 성능을 많이 저하시킬 수 있으므로 함수 객체를 사용한다.

 

  ▷ 함수객체를 이용한 callback 함수 전달

  • 함수객체 : 함수처럼 사용될 수 있는 객체
  • 함수 호출 연산자 (bool operator() ~ )를 정의하면 함수객체로 사용할 수 있다.
  • 정렬함수의 인수로 함수 대신 함수객체를 전달하면 inline 함수를 인수로 넘겨준 것의 효과를 볼 수 있다. 
/* 함수객체 정의 */
template<typename T> class GREATER {
public:
	bool operator()(const T& a, const T& b) const {
    	  return a > b;
	}
};
/* 예시 1 */
GREATER<int> greaterthan;
if (greaterthan(6, 5))
	cout << "6이 5보다 크다." << endl;

void f(vector<int>& iv) {
    // 내림차순 정렬
    sort(iv.begin(), iv.end(), GREATER<int>());   // default 생성자 동작
} 

 

 

 

STL 활용 예 - map

 ●  map

  ▷ map의 정의

  • 저장하는 데이터의 형태 : (key, value)의 쌍
  • key를 이용해서 데이터에 직접접근할 수 있는 연상 컨테이너

    - key : 데이터 집합에서 특정 데이터를 검색하거나 데이터 집합을 정렬할 때 처리의 기준이 되는 속성이다.
    - (이름, 전화번호) 에서 key를 이름으로 검색하면 전화번호를 찾을 수 있다. 
  • map에 저장되는 데이터는 key가 모두 다르다.
  • 트리 형태의 데이터 구조를 이용해서 검색 시간이 데이터 수의 log 함수에 비레한다.
  • 헤더파일 <map>을 사용한다.

 

 ●  map 이용

  ▷ map 객체 선언

map<KeyType, ValueType, Traits> objName;

// KeyType : key 자료형
// ValueType : key와 연관된 value 데이터 자료형
// Traits : map 내에서의 상대적 순서를 결정하는 함수객체의 클래스로
//         default는 less<KeyType>
  • key가 이름, value가 주소, 각각 string 타입일 때, (이름, 주소) 쌍 저장하는 map 객체는 아래와 같이 선언한다.
map<string, string> addrbook;

 

  ▷데이터 저장

  • insert( ) 함수 : 데이터 삽입 기능
map<string, string> addrbook;
addrbook.insert(make_pair("김남준", "고양시"));
addrbook.insert({"김석진", "과천시"});
addrbook.insert({"민윤기", "대구광역시"});

// pair : first와 second라는 2개의 데이터 멤버를 포함하는 template 구조체
// make_pair : pair 객체를 반환하는 함수 template
// 동일한 key를 갖는 데이터가 이미 존재할 경우 삽입이 이루어지지 않는다.

 

  • [ ] 연산자 : key를 이용한 데이터 직접접근

addrbook (초기 상태)

김남준 고양시
김석진 과천시
민윤기 대구광역시
cout << addrbook["김석진"];    // "과천시" 출력
addrbook["정호석"] = "광주광역시";  // ("정호석", "광주광역시") 삽입
addrbook["김남준"] = "서울특별시";  // "김남준"의 데이터가 수정된다. 

addrbook (수정 후 상태)

김남준 서울특별시
김석진 과천시
민윤기 대구광역시
정호석 광주광역시

 

  • find( ) 함수를 이용한 검색 - 지정된 key를 가지는 데이터를 가리키는 반복자 반환
auto it = addrbook.find("정호석"); 
// ㄴ-> it는 pair 객체 ("정호석", "광주광역시")를 가리킨다.

auto it = addrbook.find("박지민");
// ㄴ-> it에 addrbook.end()가 저장된다. 왜냐면 박지민이라는 key를 가진 데이터가 없기 때문에
// 마지막 데이터의 그 다음 위치를 가리킨다. 

 

  • erase( ) 함수를 이용한 데이터 삭제

    1) 삭제할 데이터를 가리키는 반복자를 지정하는 방법

addrbook.erase(it1);   //it가 가리키는 데이터삭제

     2) 삭제할 데이터를 가리키는 반복자의 범위를 지정하는 방법

addrbook.erase(it1, it2);   // [it1, it2) 범위의 데이터 삭제

     3) key를 지정하여 데이터를 삭제하는 방법

addrbook.erase("민윤기");  // key가 "민윤기"인 데이터 삭제

 

 ●  map의 활용 예 - PBookMap.cpp

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

template<typename T>
class LESS_T {
public:
    bool operator()(const T& a, const T& b) const {
        return a < b;
    }
};

int main()
{
    map<string, string, LESS_T<string>> pBook {
        { "Kimnamjoon", "1994-09-12"},
        { "Kimseokjin", "1992-12-04"}
    };
    pBook["MinYoongi"] = "1993-03-09";
    
    pBook.insert(make_pair("Junghoseok", "1994-02-18"));
    pBook.insert({"KimTaehyung", "1995-12-30"});
    
    for (auto pb = pBook.begin(); pb != pBook.end(); ++pb)
        cout << pb->first << "  " << pb->second << endl;
    cout << pBook.size() << "명이 등록되어 있습니다.";
    
    string str;
    cout << endl << "찾을 이름: ";
    cin >> str;
    auto result = pBook.find(str);
    if (result != pBook.end()) {
        cout << result->first << "생년월일은 ";
        cout << result->second << " 입니다." << endl;
    }
    else
        cout << str << "씨를 찾을 수가 없습니다." << endl;
    return 0;
}

 

  ▷PBookMap.cpp 출력결과

 

 

 추가사항

 ●  decltype을 이용한 자료형 추론

  ▷ decltype(e)

  • 컴파일러가 e의 자료형을 추론하게 한다.
  • e의 값을 실제로 계산하는 건 아니다.

  ▷ decltype(e)의 자료형 결정 규칙

  • e가 명칭이거나 클래스 멤버에 대한 액세스인 경우 decltype(e)는 e의 자료형이다.
  • e가 x-value인 경우 decltype(e)는 e의 자료형에 대한 r-value 참조다.
    (x-value : 함수의 r-value 참조를 return해주는 return 타입) 
  • e가 l-value인 경우 decltype(e)는 e의 자료형에 대한 l-value 참조다.
  • 그 외의 경우 decltype(e)는 e의 자료형이다.

  ▷ decltype(e)의 자료형 추론 예시

int x = 10;
double y = 3.14;
const int&& f();
struct A { double val; };
const A* p;

decltype(x) a = x;      // a는 int형
decltype(x + y) b; 		// b는 double형
decltype(++x) c = a;	// c는 int형
decltype(int{} + double{}) d;	// d는 double형
decltype(f()) f = 20;			// f는 const int&&형
decltype(p->val) g;				// g는 double형
decltype((p->val)) h = y;		// h는 const double&형, 괄호 안에 넣어서 수식을 가리킴

 

  ▷ decltype의 활용 예시 - 함수선언

  • decltype을 이용해서 함수의 자료형을 선언해주는 경우, template 이용하면 이 기능이 필요해짐 ㅎ
// 1번
double add(int a, double h) {
	return a + b;
}

// 2번
decltype(int{} + double{}) add(int a, double b){
	return a + b;
}

// 3번 - 오류
decltype(a + b) add(int a, double b){
	return a + b;
}

// 4번 - 후행반환형 trailing return type, 리턴타입을 뒤쪽에 쓰는것 (C++!1부터 가능)
auto add(int a + double b) -> decltype(int a, double b){
	return a + b;
}

 

  ▷ 여러 자료형이 혼합된 템플릿에서의 decltype 활용

  • add(const Vec<T1>& v1, const Vec<T2>& v2) 에서 아직 자료형이 정해지지 않은 상태라 어떤 형태로 return할 지 template을 정의할 때는 정해지지 않았다. -> decltype으로 자료형 추론한다. 
  • 이 때 가독성을 위해 auto를 활용해서 후행반환형으로 써서 가독성을 높여준다.
template <typename T> class Vec {
    int n;
    T* arr;
public:
    Vec(int d, const T* a = nullptr);
    .. // size(), [] operator 멤버함수 정의
};

template <typename T1, typename T2>
Vec<decltype(T1{}+T2{})> add(const Vec<T1>& v1, const Vec<T2>& v2)
auto add(const Vec<T1>& v1, const Vec<T2>& v2) -> Vec<decltype(T1{}+T2{})>
{
    Vec<decltype(T1{}+T2{})> tVec(v1.size());
    for (int i = 0; i < v1.size(); i++)  // 벡터 덧셈 연산
        tVec.arr[i] = v1[i] + v2[i];
    return tVec;
}

 

728x90
반응형