2012-01-16 105 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

但请注意不要改变给定排序容器中元素的排序。