Как выставить карту указателей как карту константных указателей? - PullRequest
5 голосов
/ 30 июня 2011

У меня есть класс с std :: map указателей в качестве члена.Теперь я хотел бы показать этот элемент только для чтения: изменение не разрешено ни для карты, ни для объектов, на которые указывают.Внутренне мне нужно, чтобы эти указатели были неконстантными, и я хочу представить их как const.

У меня есть решение, которое компилируется по крайней мере, но я хотел бы знать, есть ли какие-то скрытые проблемы, которые яЯ столкнусь с этим.

class A
{
public:
  const std::map<int, const float*>& GetMap() const { return *(reinterpret_cast< const std::map<int, const float*>* >( &m_Map)); }

private:
  std::map<int, float*> m_Map;
};

Есть возможная проблема, о которой я могу подумать: если внутреннее расположение std :: map отличается для карт указателей и карт указателей const, то это вызовет уродливостьошибок.Но я не могу придумать ни одной разумной причины, почему это так.У кого-нибудь есть идеи?

Чтобы уточнить: я знаю, что это хак, и есть более безопасные решения (например, отдельные функции доступа).Мне просто интересно, сломается ли это сразу из-за некоторой части информации, которую я пропускаю.

Ответы [ 4 ]

9 голосов
/ 30 июня 2011

Это, конечно, неопределенное (РЕДАКТИРОВАТЬ: похоже, на самом деле это только неопределенное) поведение, потому что две карты (с языковой точки зрения) являются совершенно не связанными типами.Может показаться, что это работает сейчас, но когда-нибудь оно сломается и вызовет кучу головных болей.

Считаете ли вы, что вместо того, чтобы раскрывать детали реализации (что вы используете карту внутри), вы могли бы предоставить const_iterators и find метод для открытого интерфейса вашего класса вместо этого?

РЕДАКТИРОВАТЬ: См. 5.2.10 / 7:

Указатель на объект может быть явно преобразован в указательк объекту другого типа.65) За исключением того, что преобразование r-значения типа «указатель на T1» в тип «указатель на T2» (где T1 и T2 являются типами объектов и где требования к выравниванию T2 не являются более строгими, чем требования к T1) и обратно в исходныйtype возвращает исходное значение указателя, результат такого преобразования указателя не определен.

Из этой цитаты мы заключаем, что приведение с карты с неконстантным типом значения к карте с константным типом значения имеетнеуточненное поведение.Кроме того, фактически разыменование преобразованного указателя, вероятно, нарушит строгие правила псевдонимов и приведет к неопределенному поведению.

1 голос
/ 30 июня 2011

Вы можете держать его как картуи const_cast внутри, когда вам нужно.Это некрасиво, но законно (если вы знаете, что все указанные значения на самом деле не являются постоянными).

По крайней мере, это не связано с неопределенным поведением, что, я уверен, ваше решение делает.Хотя, как вы говорите, это, вероятно, будет работать большую часть времени на большинстве платформ.

0 голосов
/ 26 ноября 2012

Одна веская причина, по которой это может вызвать проблемы: даже если двоичная реализация, как правило, одинакова (и обычно это так, но кто знает), тогда типы по-прежнему различны.Некоторые контейнеры могут использовать некоторые статические (или теперь TLS в C ++ 11) поля (например, для целей оптимизации / отладки), и они должны различаться для разных типов.

Представьте, что такое поле будет(инициализируемый нулем) указатель, которому дается какое-то существенное значение в конструкторе (если он еще не назначен).Пока ни один объект этого типа не сконструирован, можно предположить, что никто не будет его отсылать, и после первого вызова конструктора можно отложить его без проверки, не является ли он ненулевым.Ваш код может создавать контейнер, который никогда не создавался, но его методы обращаются к внутреннему указателю, что затрудняет отслеживание segfault.

0 голосов
/ 30 июня 2011

Это reinterpret_cast генерирует ссылку с неопределенным поведением.Не делай этого!Используйте const_iterators.

class A {
public:
  typedef std::map<int, float*> MapType;
  typedef MapType::const_iterator const_iterator;

  const_iterator begin () const { return m_Map.begin(); }

  const_iterator end () const { return m_Map.end(); }

private:
  std::map<int, float*> m_Map;
};


void some_function () {
  A my_map;

  // Code to build the map elided

  for (A::const_iterator iter = my_map.begin(); iter < my_map.end(); ++iter) {
    do_something_with_but_not_to (*iter);
  }

Обратите внимание, что вы также можете экспортировать такие вещи, как find, которые возвращают const_iterator.

...