template <class T>
void std::vector<T>.push_back(T instance);
template <class T, Args...> // 가변 인자 템플릿
void std::vector<T>.emplace_back(T's Args...)
class Item // push_back, emplace_back 비교를 위한 클래스입니다.
{
public: int healAmount;
Item(int amount) : healAmount(amount) { cout << "일반 생성자 호출" << endl; };
Item(const Item& rhs) : healAmount(rhs.healAmount) { cout << "복사 생성자 호출" << endl; };
Item(Item&& rhs) : healAmount(move(rhs.healAmount)) { cout << "이동 생성자 호출" << endl; };
~Item() { cout << "소멸자 호출" << endl; };
};
push_back() 수행 시
- Item(1000)을 통해 임시 객체 생성
- 복사 생성자를 통해 (위엔 이동 생성자가 정의돼있으므로 이동) push_back 함수에서 또 다른 임시 객체 생성
- vector의 끝에 요소 추가
- main의 임시 객체 삭제
- vector 내 임시 객체 삭제
int main()
{
vector<Item> items;
items.push_back(Item(1000));
}
emplace_back() 수행 시
- 1000을 emplace_back 함수에 넘김
- emplace_back 내부에서 임시 객체 생성
- vector 내 임시 객체 삭제
int main()
{
vector<Item> items;
items.emplace_back(1000);
}
불필요한 복사를 막는다는 점에서 emplace_back이 상당히 우수해보인다.
하지만 우리는 emplace_back보다 push_back를 선호하며, 실제로 많이 사용한다.
그 원인은 push_back과 emplace_back의 생성자 차용 방식에 있다.
push_back은 명시적인 생성자를 호출하지만, (생성자가 없을 경우 컴파일 에러가 발생함)
emplace_back은 가변 매개변수 리스트를 받기 때문에 컴파일 타임에 타당한 생성자가 존재하는 지 알 수 없다.
다음 예제의 작성 의도는 2차원 벡터 vec의 첫 번째 요소에 10을 넣고자 하는 것이다.
push_back의 경우, 명시적으로 vector<T> 타입의 인자를 요구하기 때문에 타입이 맞지 않으면 컴파일 에러를 띄운다.
하지만 emplace_back의 경우 인자에 맞춰서 모든 생성자를 수행할 수 있다.
그렇기 때문에 컴파일 타임엔 저것이 맞는 지 틀리는 지도 알 수 없을 뿐더러, 위의 결과는 다음과 같이 나온다.
이와 같이 emplace_back은 이동 간의 비용을 절약해주지만 컴파일 타임에 모든 문제를 잡아낼 수 없다는
치명적인 리스크가 존재한다.
이동 작업의 cost가 방대한 경우가 아니라면 굳이 사용할 필요 없을 것 같다.
실제로 컴파일러의 최적화에 따라 push_back과 속도도 크게 차이나지 않는다고 한다.
https://gumeo.github.io/post/emplace-back/
'C++' 카테고리의 다른 글
[C++] deque, stack, queue, priority_queue (0) | 2021.12.02 |
---|---|
[C++] vector와 list (0) | 2021.12.01 |
[C++] map과 unordered_map (0) | 2021.12.01 |