Abseil Tip 112 emplace vs. push_back

한글 번역


title: “Tip of the Week #112: emplace vs. push_back”
layout: tips
sidenav: side-nav-tips.html
published: true
permalink: tips/112
type: markdown
order: “112”

원래 게시 날짜: 2016-02-25, totw/112
작성자: Geoff Romer (gromer@google.com)
개정일: 2017-08-30

“우리가 힘을 덜 사용할수록, 우리의 힘은 더 커질 것이다.” — 토머스 제퍼슨


C++11은 컨테이너에 항목을 삽입하는 강력한 새로운 방법을 도입했습니다: emplace 메서드입니다. 이 메서드는 객체의 생성자를 사용하여 컨테이너 내부에서 객체를 바로 생성할 수 있도록 합니다. 여기에는 이동 및 복사 생성자도 포함되므로, pushinsert 메서드를 사용할 수 있는 곳이면 어디든지 emplace 메서드도 동일하게 사용할 수 있습니다:

std::vector<string> my_vec;
my_vec.push_back("foo");     // OK, 그래서...
my_vec.emplace_back("foo");  // 이것도 OK, 결과는 동일

std::set<string> my_set;
my_set.insert("foo");        // 여기서도 동일: insert 호출을
my_set.emplace("foo");       // emplace 호출로 변경 가능.

이것은 당연히 다음과 같은 질문을 제기합니다:
어떤 것을 사용해야 할까요?
차라리 push_back()insert()를 완전히 버리고 항상 emplace 메서드를 사용하는 것이 좋을까요?

이 질문에 답하기 위해 다른 질문을 해보겠습니다: 아래 두 줄의 코드가 각각 무엇을 할까요?

vec1.push_back(1<<20);
vec2.emplace_back(1<<20);

첫 번째 줄은 매우 간단합니다: 숫자 1048576을 벡터의 끝에 추가합니다.
하지만 두 번째 줄은 명확하지 않습니다. 벡터의 타입을 모르면 어떤 생성자를 호출하는지 알 수 없으므로 이 코드가 무엇을 하는지 확실히 말할 수 없습니다.
예를 들어, vec2std::vector<int>라면 첫 번째 줄과 동일하게 1048576을 끝에 추가합니다.
그러나 vec2std::vector<std::vector<int>>라면, 이 코드는 100만 개 이상의 요소를 가진 벡터를 생성하며, 메모리를 수 메가바이트 할당할 수 있습니다.


더 명확한 표현으로 push_back() 선택

따라서 동일한 인수로 push_back()emplace_back()을 모두 사용할 수 있다면, push_back()을 선택하면 코드의 의도가 더 명확해집니다.
또한 push_back()은 더 안전합니다. 예를 들어, std::vector<std::vector<int>>에서 첫 번째 벡터에 숫자를 추가하려 하지만 실수로 첨자를 생략했다고 가정해 봅시다.

my_vec.push_back(2<<20); // 컴파일 오류 발생 → 문제를 쉽게 발견
my_vec.emplace_back(2<<20); // 컴파일됨 → 런타임에 문제 발견

emplace_back()의 성능 이점

물론, 암시적 변환이 포함된 경우 emplace_back()push_back()보다 약간 더 빠를 수 있습니다. 예를 들어:

my_vec.push_back("foo");

위 코드는 문자열 리터럴에서 임시 string 객체를 생성한 뒤 이를 컨테이너에 이동시킵니다.
반면,

my_vec.emplace_back("foo");

는 컨테이너 내부에서 string 객체를 직접 생성하여 추가 이동을 방지합니다.
비용이 큰 타입에서는 emplace_back()을 사용하는 것이 성능상 더 나을 수 있습니다.
그러나 성능 차이가 미미한 경우도 많으며, 대부분의 경우 코드의 가독성과 안전성을 해치는 “최적화”는 피해야 합니다.


일반적인 권장 사항

일반적으로, 동일한 인수로 push_back()emplace_back()을 모두 사용할 수 있다면, push_back()을 선호하십시오.
마찬가지로 insert()emplace()도 동일합니다.

성능 차이가 실제로 애플리케이션 벤치마크에서 눈에 띄는 경우에만 emplace를 사용해 최적화를 고려하십시오.