C ++: STL: set: сохраненное значение constness - PullRequest
4 голосов
/ 17 января 2012

Имея следующий код:

#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");

Вопрос: я знаю, что map и multimap хранят свои значения как pair(const K, V), где K - это ключ, а V - это значение.Мы не можем изменить объект K.Но set<T> и multiset<T> сохраняют свой объект как T, а не const T.Так ПОЧЕМУ Я НУЖЕН ЭТОГО КОНСТАКТА CAST ??

Ответы [ 4 ]

12 голосов
/ 17 января 2012

В C ++ 11 set (и multiset) укажите, что iterator, а также const_iterator является постоянным итератором, т.е. вы не можете использовать его для изменения ключа. Это связано с тем, что любая модификация ключевых ключей может привести к нарушению инварианта множества. (См. 23.2.4 / 6.)

Ваш const_cast открывает дверь неопределенному поведению.

4 голосов
/ 17 января 2012

Значения в set не должны изменяться. Например, если вы изменили идентификатор своего сотрудника, он будет в неправильной позиции в наборе, и набор будет нарушен.

Ваш Сотрудник имеет три поля, и ваш набор использует поле _id в вашем operator<.

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

};

Следовательно, вам, вероятно, следует использовать map<int,Employee> вместо вашего набора, тогда вы сможете изменить имя и заголовок. Я бы также сделал поле _id Сотрудника const int _id.

(Кстати, идентификаторы, начинающиеся с _, технически зарезервированы и их следует избегать. Это никогда не доставляет мне никаких проблем, но теперь я предпочитаю ставить подчеркивание в конце имени переменной.)

2 голосов
/ 17 января 2012

В C ++ нельзя изменить ключи связанных контейнеров STL, потому что вы нарушите их порядок.Когда вы хотите изменить ключ, вы должны (1) найти существующий ключ, (2) удалить его и (3) вставить новый ключ.

К сожалению, пока это не слишкомпривлекательно, это то, как ассоциативные контейнеры работают в STL.

0 голосов
/ 21 июля 2012

Вы можете обойтись без const только с косвенным указанием.

Но будьте осторожны, чтобы не изменить порядок элементов в данном отсортированном контейнере.

...