모든 표준 연관 컨테이너는 내부에 저장되는 데이터 요소를 정렬해서 관리하며, 컨테이너의 정상적인 동작은 요소들이 정렬된 상태에서만 가능

연관 컨테이너에 들어있는 요소의 값을 바꾼다면 제대로 된 위치에 있을 가능성이 적으므로 컨테이너의 정렬 상태가 무너짐

 

map<K, V> 혹은 multimap<K, V> 타입의 객체에 저장되는 요소는 pair<const K, V>이기 때문에 map과 multiset은 키 값을 변경하는 것이 불가능

(const_cast를 사용하면 가능하지만 비권장)

map<int, string> m;

m.begin()->first = 100; // 에러

 

set과 multiset은 set<T> 혹은 multiset<T> 타입의 객체는 데이터 요소의 타입이 T이므로 직접적인 키 변경이 가능

키 변경이 가능하게 구현된 이유는 ID를 실질적인 키로 사용하여 키를 제외한 다른 데이터를 수정하는 경우(setTitle)가 있음

따라서 키가 아닌 데이터를 수정하기 때문에 set 객체를 훼손시키지 않음

class Employee
{
public:
    int getIDNumber() const;
    void setTitle(const string& title);

private:
    int id;
    string title;
};

struct IDNumberLess
{
    bool operator()(const Employee& lhs, const Employee& rhs) const
    {
        return lhs.getIDNumber() < rhs.getIDNumber();
    }
};

using EmpIDSet = set<Employee, IDNumberLess>;
EmpIDSet employeeSet;

EmpIDSet::iterator iterator = employeeSet.find(selectedID);
if(iterator != employeeSet.end())
{
    iterator->setTitle("Corporate Deity");
}

 

map과 multimap에도 동일한 법칙이 적용될 수 있을 생각이 있겠지만 표준화 위원회에서 map/multimap의 키는 const이어야 하고, set/multiset의 값은 const이면 안된다고 결정

set/multiset의 값은 const가 아니기 때문에 값을 바꾸어도 컴파일 에러가 발생하지 않기 때문에 컨테이너가 정상적으로 작동하기 위해 키에 해당하는 데이터를 변경해서는 안됨


set과 multiset의 요소가 const가 아니어도 데이터 요소의 변경을 막기 위해 iterator의 operator*/operator->를 const T&/const T*를 반환하도록 구현될 수 있음

(C++20에서는 const를 반환)

키 부분이 아닌 데이터의 변경을 위해 const가 아닌 참조자로 캐스팅

EmpIDSet::iterator iterator = employeeSet.find(selectedID);
if(iterator != employeeSet.end())
{
    const_cast<Employee&>(*iterator).setTitle("Corporate Deity");
}

 

참조자로 캐스팅하지 않는 경우 발생하는 문제

해당 캐스팅의 결과는 *i의 사본인 임시 객체이며 setTitle은 임시 객체에서 호출되므로 set의 객체를 변경하지 않는 오동작 발생

EmpIDSet::iterator iterator = employeeSet.find(selectedID);
if(iterator != employeeSet.end())
{
    static_cast<Employee>(*iterator).setTitle("Corporate Deity");
    ((Employee)(*i)).setTitle("Corporate Deity");
}

 

 

캐스팅을 사용하지 않고 안전하게 변경하는 방법

EmpIDSet::iterator iterator = employeeSet.find(selectedID);
if(iterator != employeeSet.end())
{
    EmpIDSet e(*i);
    employeeSet.erase(i++); // 반복자를 후위 증가하여 반복자 무효화 방지
    
    e.setTitle("Corporate Deity");
    employeeSet.insert(i, e);
}

+ Recent posts

목차