반응형
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
'프로그래밍 > C++' 카테고리의 다른 글
| C++ Erase-remove 관용구란 무엇인가? (0) | 2025.12.28 |
|---|---|
| emplace() vs emplace_back()의 차이점 알아보기 (0) | 2025.12.22 |
| C++ 기본 타입은 왜 std::move()로 이동되지 않는가? (0) | 2025.12.20 |
| C++ std::list erase() 사용시 연결은 왜 끊기지 않을까? (0) | 2025.12.07 |
| 이동(move)이 더 효율적인데 emplace_back()은 왜 복사하는가? (0) | 2025.12.06 |
댓글