본문 바로가기

C++

C++11 stl에 추가된 emplace(insert), emplace_back(push_back)

(퍼온자료입니다.  출처 : http://egloos.zum.com/sweeper/v/3060229)

 

0. Variadic template

 

C++11의 emplace 함수에 생긴 큰 변화는 variadic template으로 인해 가능해졌다고 할 수 있다.

Variaidic template 페이지를 충분히 숙지한 이후 아래 내용을 읽어 나가면 훨씬 이해가 쉬울 것이다.

 

 

1. C++0x의 emplace 함수

 

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

// VS2010의 vector::emplace_back
void emplace_back(_Ty&& _Val)
{      
    // insert element at end
    push_back(_STD forward<_Ty>(_Val));
}

template<typename _Valty>
void emplace_back(_Valty&& _Val)
{      
    // insert element at end
    if (this->_Mylast == this->_Myend)
        _Reserve(1);
        
    _Orphan_range(this->_Mylast, this->_Mylast);

    _Cons_val(this->_Alval, this->_Mylast, _STD forward<_Valty>(_Val));
    ++this->_Mylast;
}

즉, C++0x까지의 emplace 계열 함수는 외부에서 생성된 객체를 넘기는 방식이었기에, 

emplace 계열 함수를 쓰더라도, 외부에서 객체 생성 -> 이동 -> 객체 파괴가 이루어졌던 것이다.

 

물론, 객체 생성 -> 복사 -> 객체 파괴보다는 성능상 이득이 있을 수 있다.

(이동을 어떻게 짰느냐에 따라...)

 

 

2. C++11의 emplace 함수 

 

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

// VS2013의 vector::emplace_back

template<typename... _Valty>
void emplace_back(_Valty&&... _Val)
{
    // insert by moving into element at end
    if (this->_Mylast == this->_Myend)
        _Reserve(1);

    _Orphan_range(this->_Mylast, this->_Mylast);

    this->_Getal().construct(this->_Mylast, _STD forward<_Valty>(_Val)...);            
    ++this->_Mylast;
}

 

C++11의 emplace 계열 함수는 객체 생성자의 인자들을 넘겨,

컨테이너 내부에서 생성 후 추가하는 방식을 사용하기에, 임시 객체를 아예 생기지 않게 하거나, 그 횟수를 줄일 수 있다.

 

우선 vector의 emplace_back 함수의 예제를 살펴보자. (cppreference.com의 예제 도용 ㅋ)

아래 예제에서는 임시 객체 생성을 완전히 회피할 수 있다.

#include <vector>
#include <string>
#include <iostream>

using namespace std;

struct President
{
    string name;
    string country;
    int year;

    President(string p_name, string p_country, int p_year)
        : name(move(p_name)), country(move(p_country)), year(p_year)
    {
        cout << "I am being constructed.\n";
    }

    President(const President& other)
        : name(move(other.name)), country(move(other.country)), year(other.year)
    {
        cout << "I am being copied.\n";
    }


    President(President&& other)
        : name(move(other.name)), country(move(other.country)), year(other.year)
    {
        cout << "I am being moved.\n";
    }

    ~President()
    {
        cout << "I am being destructed.\n";
    }


    President& operator=(const President& other) = default;
};


int main()
{
    // VS2013의 emplace_back
    // vector 내부에서 생성 -> 컨테이터에 추가하기에 임시 객체 생성 X
    vector<President> elections;
    elections.emplace_back("Nelson Mandela", "South Africa", 1994);

    // VS2010의 emplace_back 역시 아래 push_back과 동일한 방식으로만 사용이 가능했었다
    // 외부에서 생성 -> 벡터로 이동 -> 외부 객체 파괴가 발생한다
    vector<President> reElections;
    reElections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936));

    for (President const& president: elections)
    {
        cout << president.name << " was elected president of "
             << president.country << " in " << president.year << ".\n";
    }

    for (President const& president: reElections)
    {
        cout << president.name << " was re-elected president of "
             << president.country << " in " << president.year << ".\n";
    }
}

 

 

std::map의 emplace 함수도 예제를 한번 살펴 보도록 하자.

아래 예제에서는 임시 객체가 한 번 덜 생성되는 것을 확인할 수 있다.

 

typedef map<int, President> ElectionMap;
ElectionMap elections;

////////////////////////////////////////////////////////////////////////////////
/// 기존의 insert
////////////////////////////////////////////////////////////////////////////////
{
    // p 객체 생성
    President p("Nelson Mandela", "South Africa", 1994);

    // p 객체 복사 생성 for pair -> p 객체 이동
    // 아래 두 문장은 동일하다
    //elections.insert(ElectionMap::value_type(1, p));
    elections.insert(make_pair(1, p));

    // 이 스코프가 종료되면,
    // President p("Nelson Mandela", "South Africa", 1994);에서 생성된 객체 파괴
    // ElectionMap::value_type(1, p)에서 생성된 임시 객체 파괴
    // map 소멸되면서 보관되어 있던 원소 파괴
}

////////////////////////////////////////////////////////////////////////////////
/// C++11의 emplace
////////////////////////////////////////////////////////////////////////////////
{
    // President 객체 생성 -> 객체 이동 후 바로 컨테이너에 추가
    elections.emplace(1, President("Nelson Mandela", "South Africa", 1994));

    // 이 스코프가 종료되면
    // President("Nelson Mandela", "South Africa", 1994)에서 생성된 객체 파괴
    // map 소멸되면서 보관되어 있던 원소 파괴
}

 

참고로, 각 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

'C++' 카테고리의 다른 글

C++11 람다 함수 (Lambda Function)  (0) 2020.04.26
C++11 가변인자 템플릿 (Variadic template)  (0) 2020.04.12
C++11 enum class  (1) 2020.04.12
유니코드 문자열 처리  (0) 2020.04.12
printf문의 서식지정자  (0) 2020.04.12