0. Variadic template

C++11의 emplace 함수에 생긴 큰 변화는 variadic template으로 인해 가능해졌다고 할 수 있다.
Variaidic template 페이지를 충분히 숙지한 이후 아래 내용을 읽어 나가면 훨씬 이해가 쉬울 것이다.


1. C++0x의 emplace 함수

VS2010까지의 emplace 함수들은 단순히 rvalue-reference를 인자로 받아 기존의 추가 함수를 호출하는 형식이었다,

  1. // VS2010의 vector::emplace_back
  2. void emplace_back(_Ty&& _Val)
  3. {      
  4.     // insert element at end
  5.     push_back(_STD forward<_Ty>(_Val));
  6. }
  7.  
  8. template<typename _Valty>
  9. void emplace_back(_Valty&& _Val)
  10. {      
  11.     // insert element at end
  12.     if (this->_Mylast == this->_Myend)
  13.         _Reserve(1);
  14.     _Orphan_range(this->_Mylast, this->_Mylast);
  15.                
  16.     _Cons_val(this->_Alval, this->_Mylast, _STD forward<_Valty>(_Val));
  17.     ++this->_Mylast;
  18. }

즉, C++0x까지의 emplace 계열 함수는 외부에서 생성된 객체를 넘기는 방식이었기에, 
emplace 계열 함수를 쓰더라도, 외부에서 객체 생성 -> 이동 -> 객체 파괴가 이루어졌던 것이다.

물론, 객체 생성 -> 복사 -> 객체 파괴보다는 성능상 이득이 있을 수 있다.
(이동을 어떻게 짰느냐에 따라...)


2. C++11의 emplace 함수 

emplace 함수들이 variadic template으로 인해 진정한 emplace 계열 함수로 거듭나게 되었다.

  1. // VS2013의 vector::emplace_back
  2. template<typename... _Valty>
  3. void emplace_back(_Valty&&... _Val)
  4. {
  5.     // insert by moving into element at end
  6.     if (this->_Mylast == this->_Myend)
  7.         _Reserve(1);
  8.     _Orphan_range(this->_Mylast, this->_Mylast);
  9.  
  10.     this->_Getal().construct(this->_Mylast, _STD forward<_Valty>(_Val)...);            
  11.     ++this->_Mylast;
  12. }

C++11의 emplace 계열 함수는 객체 생성자의 인자들을 넘겨,
컨테이너 내부에서 생성 후 추가하는 방식을 사용하기에, 임시 객체를 아예 생기지 않게 하거나, 그 횟수를 줄일 수 있다.

우선 vector의 emplace_back 함수의 예제를 살펴보자. (cppreference.com의 예제 도용 ㅋ)
아래 예제에서는 임시 객체 생성을 완전히 회피할 수 있다.

  1. #include <vector>
  2. #include <string>
  3. #include <iostream>
  4.  
  5. using namespace std;
  6.  
  7. struct President
  8. {
  9.     string name;
  10.     string country;
  11.     int year;
  12.  
  13.     President(string p_name, string p_country, int p_year)
  14.         : name(move(p_name)), country(move(p_country)), year(p_year)
  15.     {
  16.         cout << "I am being constructed.\n";
  17.     }
  18.  
  19.     President(const President& other)
  20.         : name(move(other.name)), country(move(other.country)), year(other.year)
  21.     {
  22.         cout << "I am being copied.\n";
  23.     }
  24.  
  25.     President(President&& other)
  26.         : name(move(other.name)), country(move(other.country)), year(other.year)
  27.     {
  28.         cout << "I am being moved.\n";
  29.     }
  30.  
  31.     ~President()
  32.     {
  33.         cout << "I am being destructed.\n";
  34.     }
  35.  
  36.     President& operator=(const President& other) = default;
  37. };
  38.  
  39. int main()
  40. {
  41.     // VS2013의 emplace_back
  42.     // vector 내부에서 생성 -> 컨테이터에 추가하기에 임시 객체 생성 X
  43.     vector<President> elections;
  44.     elections.emplace_back("Nelson Mandela""South Africa"1994);
  45.  
  46.     // VS2010의 emplace_back 역시 아래 push_back과 동일한 방식으로만 사용이 가능했었다
  47.     // 외부에서 생성 -> 벡터로 이동 -> 외부 객체 파괴가 발생한다
  48.     vector<President> reElections;
  49.     reElections.push_back(President("Franklin Delano Roosevelt""the USA"1936));
  50.  
  51.     for (President const& president: elections)
  52.     {
  53.         cout << president.name << " was elected president of "
  54.              << president.country << " in " << president.year << ".\n";
  55.     }
  56.     for (President const& president: reElections)
  57.     {
  58.         cout << president.name << " was re-elected president of "
  59.              << president.country << " in " << president.year << ".\n";
  60.     }
  61. }

std::map의 emplace 함수도 예제를 한번 살펴 보도록 하자.
아래 예제에서는 임시 객체가 한 번 덜 생성되는 것을 확인할 수 있다.

  1. typedef map<int, President> ElectionMap;
  2. ElectionMap elections;
  3.  
  4. ////////////////////////////////////////////////////////////////////////////////
  5. /// 기존의 insert
  6. ////////////////////////////////////////////////////////////////////////////////
  7. {
  8.     // p 객체 생성
  9.     President p("Nelson Mandela""South Africa"1994);
  10.  
  11.     // p 객체 복사 생성 for pair -> p 객체 이동
  12.     // 아래 두 문장은 동일하다
  13.     //elections.insert(ElectionMap::value_type(1, p));
  14.     elections.insert(make_pair(1, p));
  15.  
  16.     // 이 스코프가 종료되면,
  17.     // President p("Nelson Mandela", "South Africa", 1994);에서 생성된 객체 파괴
  18.     // ElectionMap::value_type(1, p)에서 생성된 임시 객체 파괴
  19.     // map 소멸되면서 보관되어 있던 원소 파괴
  20. }
  21.  
  22. ////////////////////////////////////////////////////////////////////////////////
  23. /// C++11의 emplace
  24. ////////////////////////////////////////////////////////////////////////////////
  25. {
  26.     // President 객체 생성 -> 객체 이동 후 바로 컨테이너에 추가
  27.     elections.emplace(1, President("Nelson Mandela""South Africa"1994));
  28.  
  29.     // 이 스코프가 종료되면
  30.     // President("Nelson Mandela", "South Africa", 1994)에서 생성된 객체 파괴
  31.     // map 소멸되면서 보관되어 있던 원소 파괴
  32. }

참고로, 각 STL 컨테이너들이 지원하는 emplace 계열 함수들은 다음과 같다.

1) vector
  • emplace
  • emplace_back
2) deque / list
  • emplace
  • emplace_back
  • emplace_front
3) foward_list
  • emplace_front
  • emplace_after
4) set / map / unoreder_set / unordered_map
  • emplace
  • emplace_hint



ref : http://egloos.zum.com/sweeper/v/3060229


반응형

+ Recent posts