본문 바로가기
프로그래밍/C++

C++ emplace_back()을 무조건 권장하지 않는 이유는?

by bantomak 2025. 12. 22.
반응형

push_back() vs emplace_back()

해당 대결에서 당연히 바로 생성(in-place construction)을 지원하는 emplace_back()을 무조건 권장될 것이라고 생각했는데 현실은 달랐다. C++ Core Guidelines에서 emplace_back()은 무조건 권장되지 않는다. 이유는 무엇일까?

좋아 보이기만 했던 emplace_back()의 문제점

emplace_back은 단순 삽입 API가 아니라, 컨테이너 내부 타입의 모든 생성자를 외부로 공개하는 API이다.

  • 생성자 오버로드가 많을수록
  • initializer_list가 있을수록
  • explicit 생성가 있을수록

해당 경우에 해당할수록 호출자가 의도하지 않은 생성자가 선택될 가능성이 상승한다. Core Guidelines는 이를 'API surface area 증가'라고 부른다.

explicit 키워드 무력화

class User {
public:
    // 실수로 숫자가 User 객체로 변환되는 것을 막기 위해 explicit 선언
    explicit User(int id) : id_(id) {
        std::cout << "User ID " << id_ << " 생성됨" << std::endl;
    }

private:
    int id_;
};

int main() {
    std::vector<User> users;

    // 1. push_back의 경우 (컴파일 에러 발생)
    // users.push_back(10); 
    // error: 'User'의 explicit 생성자를 호출할 수 없음. 

    // 2. emplace_back의 경우 (정상 동작)
    users.emplace_back(10); 
    // 실수로 숫자를 넣었음에도 User 객체가 생성되어 버림.
}

emplace_back() 오히려 가독성 저해

  • 0은 어떤 타입인가?
  • 어떤 생성자를 호출했는가?
  • 모호함을 증가시킬 가능성 존재
emplace_back(0);
push_back(Data{0});

push_back은 의도적으로 명확함. 생성 타입과 어떤 생성자를 호출했는지 한눈에 보임

push_back() vs emplace_back() 예제코드

바로 생성(in-place construction) 지원 말고는 두 함수의 차이는 사실상 없다.

// l-value 생성
MyData data;
data.ID = 1;
data.Name = "kane";
data.Address = "ganam-gu";

std::vector<MyData> myVec;
myVec.reserve(2);

myVec.emplace_back(MyData(2, "jane", "sonpa-gu")); // 임시 객체 생성됨
myVec.emplace_back(std::move(data)); // move
myVec.emplace_back(2, "jane", "sonpa-gu"); // in-place

myVec.push_back(MyData(2, "jane", "sonpa-gu")); // 임시 객체 생성됨
myVec.push_back(std::move(data)); // move
myVec.push_back(2, "jane", "sonpa-gu"); // error

성능 이점 생각보다 작고, 이점 또한 조건부로 발동

  • C++ 17 이후 copy elision, move elision
  • RVO / NRVO
push_back(T(args...));
emplace_back(args...);

두 코드의 차이가 거의 없다. 성능 차이는 대부분 무의미해졌다.

정리하자면

  • 항상 emplace_back()을 써라 > 아님
  • push_back()보다 emplace_back()이 더 좋다 > 아님
  • 객체가 이미 있다면?  push_back()
  • 생성자 인자를 그대로 넘기고 싶다면? emplace_back()
  • 의도가 명확하지 않다면? 차라리 push_back()

고리타분하게도 의도에 맞게 선택적으로 사용하라가 정답인 것으로 보인다.

함께 읽으면 좋은 글

 

C++ push_back()과 emplace_back() 차이

push_back() 이미 만들어진 객체를 복사 또는 이동해서 추가push_back은 이미 생성된 객체를 추가할 때 사용한다.내부적으로 복사 생성자 혹은 이동 생성자가 호출된다.std::vector v;std::string s = "Hello";v.p

jettstream.tistory.com

댓글