map::operator[]는 AddOrUpdate 기능을 수행하도록 설계되어 키를 점검하여 들어있지 않은 경우에 pair가 추가되고, 들어있는 경우에는 value가 업데이트하고 값에 대한 참조자를 반환
키가 있지 않은 경우에 값 타입의 기본 생성자를 사용하여 pair 객체를 새로 생성한 후 operator=를 통해 복사되므로 불필요한 기본 생성자와 대입 연산이 이루어짐
class Widget
{
public:
Widget();
Widget(double Weight);
Widget& operator=(double Weight);
};
map<int, Widget> m;
m[1] = 1.50;
불필요한 기본 생성자와 대입 연산 제거되므로 추가를 하고 싶다면 operator[] 보다는 insert를 쓰는 것이 좋음
m.insert(map<int, Widget>::value_type(1, 1.50));
insert를 호출할 때 pair의 생성자와 소멸자, Widget의 생성자와 소멸자가 반드시 호출됨
반면 키가 이미 존재할 때에 operator[]는 pair의 생성자와 소멸자, Widget의 생성자와 소멸자의 비용이 들지 않음
결론
map에 추가할 경우 insert가 효율적으로 좋고, 갱신할 경우 operator[]이 효율적으로 좋음
추가와 갱신을 한번에 효율을 잡을 수 있는 함수 구현
k의 위치 혹은 삽입 위치를 찾기 위해 lower_bound 호출하고 동등성 검사
단축 정보(hint) : iterator가 k와 동등한 키를 가진 새 요소의 위치를 정확히 가리킴
표준안에 의하면 단축 정보가 제대로 세팅되어 있으면 insert의 수행 시간은 상수 시간
template<typename MapType, typename KeyArgType, typename ValueArgType>
typename MapType::iterator efficientAddOrUpdate(MapType& m, const KeyArgType& k, const ValueArgType& v)
{
typename MapType::iterator iterator = m.lower_bound(k);
if(iterator != m.end() && !(m.key_comp()(k, iterator->first)))
{
iterator->second = v;
return iterator;
}
else
{
return m.insert(iterator, typename MapType::value_type(k, v)));
}
}
KeyArgType과 ValueArgType이 맵에 저장된 타입으로 변환될 수만 있다면(Widget의 경우 operator=) 다른 타입이어도 됨
KeyArgType과 ValueArgType 대신에 MapType::key_type과 MapType::mapped_type을 사용한다면 타입의 불필요한 생성자, 소멸자 비용을 추가로 지불할 수 있음
(Widget의 경우 Widget(double Widget))
efficientAddOrUpdate(m, 1, 1.5);