본문 바로가기
개발 🛠💻/C++, Modern C++

[C++] emplace_back vs push_back => 객체 이동의 cost 비교

by 승지 T-Story 2025. 1. 22.

emplace_back과 push_back은 '더 효율적인' 같은 수식어는 붙을 수 없다 입니다.
 

#include<vector>
#include<iostream>

int main(){
  // Basic example
  std::vector<int> foo;
  foo.push_back(10);
  foo.emplace_back(20);

  // More tricky example
  std::vector<std::vector<int>> foo_bar;
  //foo_bar.push_back(10); // Throws error!!!!
  foo_bar.emplace_back(20); // Compiles with no issue
  std::cout << "foo_bar size: " << foo_bar.size() << "\n";
  std::cout << "foo_bar[0] size: " << foo_bar[0].size() << "\n";
  return 0;
}

이 코드를 보면, 'std::vector<std::vector<int>> foo_bar' 로 이중 벡터가 선언되어 있습니다.
push_back을 통하여 데이터를 넣으려면 

vector<int> v{1, 2};
foo_bar.push_back(std::move(v));

이런 형식으로 데이터를 넣어야 하는데, 

foo_bar.push_back(10); // Throws error!!!!

다음과 같이 넣어버리면, 오류가 발생합니다.
하지만 emplace_back에서는 오류가 발생하지 않습니다.

  foo_bar.emplace_back(20); // Compiles with no issue

이는 foo_bar 안의 vector[0] 에게 20개의 새로운 요소를 생성하라는 것과 동일하기 때문에 이 일이 발생합니다.
( 즉, foo_bar안에서 20이라는 파라미터가 들어가게되면, 내부에서는 vector의 마지막 다음 요소에 20이란 값을 가지고 새로운 객체를 생성한다. 하지만 vector는 20이란 숫자가 들어가면, 20개의 크기를 할당하므로 이런일이 발생한다. )
 

emplace_back 내부의 생성

또한 축소 변환 등, 암시적인 변환에 대해 막기가 힘듭니다. 다음 예제를 보자.

#include <vector>
class A {
 public:
  explicit A(int /*unused*/) {}
};
int main() {
  double foo = 4.5;
  std::vector<A> a_vec{};
  a_vec.emplace_back(foo); // No warning with Wconversion
  //A a(foo); // Gives compiler warning with Wconversion as expected
}

컴파일 타임에서 에러를 잡아내지 못하기 때문에, 런타임에서 의도하지 않은 행동이 발생하게 된다.
 
위의 링크에서는 '다중 파라미터'를 이용하여 생성자를 호출할 때 사용하라고 적혀있다.

class Image {
  Image(size_t w, size_t h);
};

std::vector<Image> images;
images.emplace_back(2000, 1000);

image 같은 데이터를 push_back을 이용하여 추가하려면 새로운 객체를 생성하고 push_back을 호출하여야 하는데,
생성자만을 이용하여 호출하면 '이동'을 하지 않으므로 조금 더 가볍게 사용할 수 있습니다.
즉 -> '이동' 작업이 비쌀 때만 사용하여야 합니다.
 
또한 C++17에서부터는 emplace_back을 사용하면, 삽입된 요소에 대한 참조를 반환합니다. 이를 사용할때도 사용합니다.
 
https://openmynotepad.tistory.com/10

댓글