[C++] STL emplace 함수 설명 및 사용법

emplace 함수

std::emplace 함수는 STL 컨테이너( vector, list, set, map, deque 등 )에서 사용가능한, 새로운 원소를 삽입하는 함수입니다.

 

이와 비슷한 함수로  vector의 emplace_back, list의 emplace_front, emplace_back, map의 emplace_hine 등이 있지만,

함수의 필요에 따라, 필요한 인자를 더 받는 것으로, 동작하는 원리는 같습니다.

 

그리고, 원시 데이터( int, float 등 )를 처리하는 경우엔 insertemplace 함수와의 차이점은 없습니다.

하지만, 객체 데이터를 다루는 경우엔 사용하는 방법에 따라 차이가 있습니다.

 

이 함수가 insertpush_back 의 함수와 다른 점은 입력된 매개 변수를 사용해 내부 삽입을 수행하고,

그걸 통해서 불필요한 객체 복사를 방지한다는 점입니다.

 

그럼, 내부 삽입을 설명하기 위해서 먼저 클래스를 작성하겠습니다.

class CData{

    int m_a;
    string m_str;

public:
    CData( int a, string str){	// 매개 변수 생성자
        m_a = a;
        m_str = str;
        cout << "Parameter Constructor\n";
    }

    CData( const CData& other){	// 복사 생성자
        m_a = other.m_a;
        m_str = other.m_str;
        cout << "Copy Constructor\n";
    }

    CData( CData&& other){	// 이동 생성자
        m_a = other.m_a;
        m_str = move(other.m_str);
        cout << "Move Constructor\n";
    }
};

이 클래스는 복사 생성자와 이동 생성자를 구현하고 있는 단순한 클래스입니다.

 

우선, 이 클래스의 객체를 vector에 넣는 과정을 보겠습니다.

int main(){

    CData d(10, "test");

    vector<CData> vec;
    vec.reserve(10);    // 재할당이 일어나는 것을 막으려고, 미리 메모리를 확보

    vec.push_back(d);
    vec.emplace_back(d);

    return 0;
}

▼출력

Parameter Constructor
Copy Constructor
Copy Constructor

이 경우, 두 함수 모두 결과가 똑같습니다.

객체 d를 복사해서, vector에 복사한 객체를 삽입합니다.

 

그리고, 데이터 이동을 통해 원소를 추가하더라도, 두 함수의 차이는 없습니다.

vec.push_back( move(d));	// d -> move(d)
vec.emplace_back( move(d));

▼출력

Parameter Constructor
Move Constructor
Move Constructor

이 때도, 두 함수의 결과는 똑같습니다.

 

그러나, emplace 함수에선 객체에 생성에 필요한 데이터만으로 객체를 삽입할 수 있는 기능이 있습니다.

int main(){

    vector<CData> vec;
    vec.reserve(10);    // 재할당이 일어나는 것을 막으려고, 미리 메모리를 확보

    cout << "push_back\n";
    vec.push_back( CData(10, "test"));

    cout << "\nemplace\n";
    vec.emplace_back( 10, "test");

    return 0;
}

▼출력

push_back
Parameter Constructor
Move Constructor

emplace
Parameter Constructor

위의 결과가 보여주듯이, emplace는 매개 변수들을 받아서, 호출하고자 하는 생성자를 유추합니다

그래서, 내부에서 유추한 생성자를 통한 객체를 생성하고, 복사나 이동의 과정 없이 생성된 객체를 바로 컨테이너에 추가합니다.

이 과정이 위에서 말한 "내부 삽입"입니다.

 

반대로, push_back에는 이런 기능이 없습니다. 

따라서, insertpush_back 같은 원소를 추가하는 함수는 일반적으로 이동 과정이 더 필요하고,

만약, 이동 생성자를 구현하지 않아서 복사 생성자가 호출된다면, 두 함수 간의 성능 차이는 더 늘어나게 될 것입니다.

 

map에서 emplace 사용

map 컨테이너에서 emplace 함수를 사용하는 예제입니다.

int main(){

    map< int, string> M;

    M.insert( make_pair(1,"korea" ));	// insert

    M.emplace( 2, "america");	// emplace

    for( auto& [key, value] : M ){
        cout << key << ": " << value << endl;
    }

    return 0;
}

▼출력

1: korea
2: america

emplace함수를 사용하면, 따로 pair를 구성할 필요 없이, 바로 원소를 map에 추가할 수 있습니다.

 

list에서 emplace 사용

list 컨테이너에서 emplace 함수를 사용하는 예제입니다.

int main(){

    list< pair<int, float> > li;

    li.insert( li.begin(), make_pair(2, 3.5));	// insert

    li.emplace( li.begin(), 3, 2.7);	// emplace

    for( const auto& x : li){
        cout << x.first << ", " << x.second << endl;
    }
    
    return 0;
}

▼출력

3, 2.7
2, 3.5

 

이번에는 tuple 객체를 emplace 함수를 통해서 list에 저장하는 예제입니다.

#include <tuple>

int main(){

    list< tuple<int, float, string> > li;

    li.insert( li.begin(), make_tuple(2, 3.5, "insert"));

    li.emplace( li.begin(), 3, 2.7, "emplace");	// tuple을 내부 생성

    for( const auto& x : li){
        cout << get<0>(x) << ", " << get<1>(x) << ", " << get<2>(x) << endl;
    }
    
    return 0;
}

▼출력

3, 2.7, emplace
2, 3.5, insert

위에서 보다시피, make_tuple 함수를 호출할 필요가 없습니다.

 

tuple의 사용법에 관한 글은 여기에서 볼 수 있습니다.

 

[C++] std::tuple 사용법

tuple 사용법tuple은 여러 개의 원소를 저장하는 데 쓰이는 C++ 표준 라이브러리의 유틸리티 클래스입니다.pair 같은 경우 2개의 원소만을 저장할 수 있는데 반면, tuple은 원소의 개수에 제약이 없습

codingembers.tistory.com

 

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유