2012-01-16 101 views
4

有以下代碼:C++:STL:集合:存儲值常量性

#include <iostream> 
#include <set> 
#include <string> 
#include <functional> 

using namespace std; 

class Employee { 
    // ... 
    int _id; 
    string _name; 
    string _title; 
public: 
    Employee(int id): _id(id) {} 

    string const &name() const { return _name; } 
    void setName(string const &newName) { _name = newName; } 

    string const &title() const { return _title; } 
    void setTitle(string const &newTitle) { _title = newTitle; } 

    int id() const { return _id; } 
}; 

struct compEmployeesByID: public binary_function<Employee, Employee, bool> { 
    bool operator()(Employee const &lhs, Employee const &rhs) { 
    return lhs.id() < rhs.id(); 
    } 
}; 

int wmain() { 
    Employee emplArr[] = {0, 1, 2, 3, 4}; 
    set<Employee, compEmployeesByID> employees(emplArr, emplArr + sizeof emplArr/sizeof emplArr[0]); 
    // ... 
    set<Employee, compEmployeesByID>::iterator iter = employees.find(2); 
    if (iter != employees.end()) 
    iter->setTitle("Supervisor"); 

    return 0; 
} 

我不能編譯具有該代碼(MSVCPP 11.0):

1> main.cpp 
1>d:\docs\programming\test01\test01\main.cpp(40): error C2662: 'Employee::setTitle' : cannot convert 'this' pointer from 'const Employee' to 'Employee &' 
1>   Conversion loses qualifiers 

這有助於編譯:

if (iter != employees.end()) 
    const_cast<Employee &>(*iter).setTitle("Supervisor"); 

問題:我知道mapmultimap將它們的值存儲爲pair(const K, V)其中K是一個鍵,V是一個值。我們不能改變K對象。但是set<T>multiset<T>將其對象存儲爲T,而不是const T。那麼爲什麼我需要這個CONST CAST?

+4

實際上,我認爲'set's * do *存儲了它們的值,以至於它們不容易修改(實際上就是'const')。如果您修改了一個值,那麼該項目可能位於該集合中的錯誤位置,因此允許修改該項目是沒有意義的。 – 2012-01-16 22:26:26

+2

'std :: unary_function'在2011年已被棄用,您可能希望用lambda替換函子。 – pmr 2012-01-16 22:34:26

+0

這是一個警告,您正在以錯誤的方式使用'set'。你的記錄有鍵和值,但你將它們存儲在'set'而不是'map'中。 – Omnifarious 2012-01-16 22:49:10

回答

12

在C++ 11集(和multiset)中指定iterator以及const_iterator是一個常量迭代器,即不能用它來修改該鍵。這是因爲他們的任何修改都會破壞集合的不變性。 (見23.2.4/6)

你的const_cast打開未定義行爲的大門。

+0

+1供參考:) – 2012-01-16 22:36:20

+0

很好的答案。應該是非常有幫助的 – DaddyM 2012-01-16 22:48:36

+0

我有ISO/IEC 14882:2011(E)標準但是23.2.4/6沒有指出'iterator'類型。相反,您可以在這裏看到** _ X :: iterator - 其值類型爲T **的迭代器類型(23.2.4) – DaddyM 2012-01-16 22:56:35

2

在C++中,您不能修改關聯的STL容器的鍵,因爲您將打斷它們的排序。當你想改變一個鍵時,你應該(1)找到現有的鍵,(2)刪除它,(3)插入新的鍵。

不幸的是,雖然這不是太吸引人,但它是關聯容器在STL中的工作方式。

+0

謝謝你的回答。應該非常有幫助。 – DaddyM 2012-01-16 22:48:56

4

set中的值不應被修改。例如,如果您修改了員工的ID,那麼它將在該組中的位置錯誤,並且該組將被打破。

您的員工有三個字段,並且您的集合正在使用operator<中的_id字段。

class Employee { 
    // ... 
    int _id; 
    string _name; 
    string _title; 

}; 

因此,你應該使用map<int,Employee>,而不是你的設置,那麼你就可以修改名稱和標題。我也會使員工的_id字段爲const int _id

(順便說一句,與_開始的名稱在技術上是保留的,應儘量避免。這是從來沒有找我麻煩,但現在我更願意把下劃線的變量名的末尾。)

+1

謝謝。幾個有用的點! – DaddyM 2012-01-16 22:46:55

0

你可以只用間接方式就可以逃脫const

但請注意不要改變給定排序容器中元素的排序。