push_back 및 emplace_back


761

나는 조금의 차이에 대한 혼란 스러워요 push_backemplace_back.

void emplace_back(Type&& _Val);
void push_back(const Type& _Val);
void push_back(Type&& _Val);

가로 push_back과부하 내가 아주의 목적은 무엇 보지 않는를 rvalue 참조 복용 emplace_back되고 있습니까?



16
(토마스가 아래에 말했듯이) 질문의 코드는 C ++ 0x가 실제로 아닌 MSVS의 C ++ 0x 에뮬레이션 에서 가져온 것입니다.
me22

5
더 좋은 논문은 open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2345.pdf 입니다. N2642는 대부분 표준에 대한 표현입니다. N2345는 아이디어를 설명하고 동기를 부여하는 논문입니다.
Alan

MSVC10에서도 단일 인수 생성자에 대한 완벽한 전달을 제공 template <class _Valty> void emplace_back(_Valty&& _Val)하는 범용 참조 를 사용 하는 버전이 explicit있습니다.
joki

관련 : push_back선호되는 경우 가 emplace_back있습니까? 내가 생각할 수있는 유일한 경우는 클래스가 어쨌든 복사 가능 T&operator=(constT&)하지만 ( ) 생성 가능하지 않은 경우 ( )지만 T(constT&), 왜 그것을 원할지는 생각할 수 없습니다.
Ben

답변:


568

방문자가 말한 것 외에도 :

void emplace_back(Type&& _Val)MSCV10에서 제공하는 기능 은 규격에 맞지 않고 중복되어 있습니다 push_back(Type&& _Val).

그러나 실제 C ++ 0x 형식 emplace_back은 실제로 유용합니다. void emplace_back(Args&&...);

대신 value_type인수의 가변 목록을 취하므로 이제 인수를 완벽하게 전달하고 개체를 컨테이너없이 컨테이너에 직접 구성 할 수 있습니다.

RVO와 이동 의미가 테이블에 가져 오는 영리성에 관계없이 push_back이 불필요한 복사 (또는 이동)를 일으킬 가능성이 여전히 복잡한 경우가 있기 때문에 유용합니다. 예를 들어,의 전통적인 insert()기능 std::map을 사용하여 임시를 생성해야합니다. 임시 std::pair<Key, Value>는으로 복사 된 다음지도로 복사됩니다.

std::map<int, Complicated> m;
int anInt = 4;
double aDouble = 5.0;
std::string aString = "C++";

// cross your finger so that the optimizer is really good
m.insert(std::make_pair(4, Complicated(anInt, aDouble, aString))); 

// should be easier for the optimizer
m.emplace(4, anInt, aDouble, aString);

그렇다면 왜 MSVC에서 올바른 버전의 emplace_back을 구현하지 않았습니까? 사실, 너무 오래 전에 버그가 발생했기 때문에 Visual C ++ 블로그 에서 같은 질문을했습니다 . 다음은 Microsoft Visual C ++ 표준 라이브러리 구현의 공식 관리자 인 Stephan T Lavavej의 답변입니다.

Q : 베타 2 임베드 기능은 현재 일종의 자리 표시 자입니까?

A : 아시다시피, 가변 템플릿은 VC10에서 구현되지 않습니다. 우리는 make_shared<T>(), 튜플 및의 새로운 것들을 위해 전 처리기 기계로 시뮬레이션합니다 <functional>. 이 전 처리기 기계는 사용 및 유지 관리가 상대적으로 어렵습니다. 또한 서브 헤더를 반복적으로 포함해야하므로 컴파일 속도에 큰 영향을줍니다. 시간 제약과 컴파일 속도 문제의 조합으로 인해 우리는 emplace 함수에서 variadic 템플릿을 시뮬레이션하지 않았습니다.

variadic 템플릿이 컴파일러에서 구현되면 내장 함수를 포함하여 라이브러리에서 템플릿을 활용할 것으로 기대할 수 있습니다. 우리는 준수를 매우 중요하게 생각하지만 불행히도 모든 것을 한꺼번에 할 수는 없습니다.

이해할 수있는 결정입니다. 전 처리기 끔찍한 요령으로 다양한 템플릿을 에뮬레이션하려고 한 번만 시도한 사람은이 물건이 얼마나 역겨운 지 알고 있습니다.


101
C ++ 문제가 아닌 MSVS10 문제라는 설명은 여기서 가장 중요한 부분입니다. 감사.
me22

11
마지막 C ++ 코드 줄이 작동하지 않는다고 생각합니다. pair<const int,Complicated>int, 다른 int, double 및 4 번째 매개 변수로 문자열을 취하는 생성자가 없습니다. 그러나 piecewise-constructor를 사용하여이 쌍 오브젝트를 직접 구성 할 있습니다. 구문은 물론 다를 것이다 :m.emplace(std::piecewise,std::forward_as_tuple(4),std::forward_as_tuple(anInt,aDouble,aString));
sellibitze

3
행복하게도 다양한 템플릿은 VS2013에 있으며 이제 미리 볼 수 있습니다.
Daniel Earwicker

11
이 답변은 vs2013의 새로운 개발 내용을 반영하도록 업데이트되어야합니까?
becko

6
나중에 비주얼 스튜디오 2013를 사용하는 경우 지금 , 당신은 "진짜"에 대한 지원이 있어야합니다 emplace_back: 그것은 가변 인자 템플릿이 추가 된 비주얼 C ++로 구현으로 이렇게 오래 msdn.microsoft.com/en-us/library/hh567368. aspx
kayleeFrye_onDeck

200

emplace_back유형의 인수를 vector::value_type사용하지 말고 대신 첨부 된 항목의 생성자에게 전달되는 가변적 인 인수를 사용하십시오.

template <class... Args> void emplace_back(Args&&... args); 

value_type복사 생성자로 전달할 수 있습니다.

인수를 전달하기 때문에 rvalue가없는 경우 컨테이너가 이동 된 사본이 아니라 "복사 된"사본을 저장한다는 의미입니다.

 std::vector<std::string> vec;
 vec.emplace_back(std::string("Hello")); // moves
 std::string s;
 vec.emplace_back(s); //copies

그러나 위의 내용과 동일해야합니다 push_back. 아마도 다음과 같은 유스 케이스를 의미합니다.

 std::vector<std::pair<std::string, std::string> > vec;
 vec.emplace_back(std::string("Hello"), std::string("world")); 
 // should end up invoking this constructor:
 //template<class U, class V> pair(U&& x, V&& y);
 //without making any copies of the strings

2
@David : 그러나 당신은 s범위 내에서 움직 였지만 그렇게 위험하지 않습니까?
Matthieu M.

2
더 이상 값을 사용하지 않을 계획이라면 위험하지 않습니다. 이동은 s를 무효화하지 않으며, 이동은 s에서 이미 수행 된 내부 메모리 할당 만 훔치고 기본 상태 (sting 할당되지 않음)로 남겨 둡니다. std :: string str;
David

4
@David : 이동 된 객체가 후속 파괴를 제외한 모든 용도에 유효한지 확실하지 않습니다.
Ben Voigt

46
vec.emplace_back("Hello")const char*인수가 생성자 에게 전달 되므로 작동 합니다 string. 이것이 요점입니다 emplace_back.
Alexandre C.

8
@BenVoigt : 이동 된 오브젝트는 유효한 (지정되지 않은) 상태 여야합니다. 그러나 반드시 작업을 수행 할 수있는 것은 아닙니다. 고려하십시오 std::vector. 빈 std::vector상태는 유효한 상태이지만 호출 할 수 없습니다 front(). 이는 전제 조건이없는 함수를 계속 호출 할 수 있음을 의미합니다 (소멸자는 전제 조건을 가질 수 없음).
David Stone

96

emplace_back다음 예에서 최적화에 대해 설명 할 수 있습니다.

들어 emplace_back생성자 A (int x_arg)호출됩니다. for push_back A (int x_arg)가 먼저 move A (A &&rhs)호출되고 이후에 호출됩니다.

물론 생성자는로 표시되어야 explicit하지만 현재 예제에서는 명시 성을 제거하는 것이 좋습니다.

#include <iostream>
#include <vector>
class A
{
public:
  A (int x_arg) : x (x_arg) { std::cout << "A (x_arg)\n"; }
  A () { x = 0; std::cout << "A ()\n"; }
  A (const A &rhs) noexcept { x = rhs.x; std::cout << "A (A &)\n"; }
  A (A &&rhs) noexcept { x = rhs.x; std::cout << "A (A &&)\n"; }

private:
  int x;
};

int main ()
{
  {
    std::vector<A> a;
    std::cout << "call emplace_back:\n";
    a.emplace_back (0);
  }
  {
    std::vector<A> a;
    std::cout << "call push_back:\n";
    a.push_back (1);
  }
  return 0;
}

산출:

call emplace_back:
A (x_arg)

call push_back:
A (x_arg)
A (A &&)

21
호출 할 때 실제로 어떻게되는지 보여줍니다 코드 예를 들어 한 emplace_backpush_back.
Shawn

v.emplace_back(x);x가 명시 적으로 이동 구성 가능하지만 명시 적으로 복사 구성 가능 한 위치 를 호출 하는 코드가 있음을 알았을 때 여기에 왔습니다 . emplace_back"암시 적으로"명시 적이라는 사실 은 추가를위한 나의 go-to 함수가 아마도이어야한다고 생각합니다 push_back. 생각?
Ben

a.emplace_back두 번째로 전화 하면 이동 생성자가 호출됩니다!
X Æ A-12


8

emplace_back적합한 구현은 vector<Object>::value_type벡터에 추가 될 때 인수를 생성자에 전달 합니다. Visual Studio는 가변 템플릿을 지원하지 않았지만 가변 템플릿을 사용하면 Visual Studio 2013 RC에서 지원되므로 적합한 서명이 추가 될 것입니다.

을 사용 emplace_back하면 인수를 vector<Object>::value_type생성자 에게 직접 전달하면 emplace_back엄밀히 말하면 함수를 위해 이동 가능하거나 복사 가능한 유형이 필요하지 않습니다 . 이 vector<NonCopyableNonMovableObject>경우 vector<Object>::value_type 복사 할 수 있거나 움직일 수있는 유형이 필요 하므로 유용하지 않습니다 .

그러나 참고 이 유용 할 수 있다고 std::map<Key, NonCopyableNonMovableObject>당신은지도에 엔트리를 할당하면, 그것은 이동하거나와는 달리, 더 이상 지금까지 복사 할 필요가 없기 때문에, vector당신이 사용할 수있는 의미 std::map도 복사 가능한입니다 매핑 유형을 효과적으로도 움직일 수 있는.


8

목록의 경우 하나 더 :

// constructs the elements in place.                                                
emplace_back("element");


//It will create new object and then copy(or move) its value of arguments.
push_back(explicitDataType{"element"});

1

구체적인 사용 사례 emplace_back: 임시 객체를 생성하여 컨테이너로 푸시해야하는 경우 emplace_back대신을 사용하십시오 push_back. 컨테이너 내에서 개체를 제자리에 만듭니다.

노트:

  1. push_back위의 경우 임시 객체를 만들어 컨테이너로 옮깁니다. 그러나 사용되는 내부 구성 emplace_back은 오브젝트를 구성한 다음 이동하는 것 (일반적으로 일부 복사가 필요함)보다 성능이 우수합니다.
  2. 일반적으로, 당신이 사용할 수있는 emplace_back대신에 push_back많은 문제없이 모든 경우에. ( 예외 참조 )
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.