如何将指针映射公开为常量指针映射

How to expose a map of pointers as a map of const pointers?

本文关键字:指针 映射 常量      更新时间:2023-10-16

我有一个类,它的成员是指针的std::映射。现在,我想以只读的方式公开该成员:映射和所指向的对象都不允许修改。在内部,我需要这些指针是非常量的,我想将它们公开为常量。

我确实有一个至少可以编译的解决方案,但我想知道这个解决方案是否会遇到任何隐藏的问题。

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指针的映射不同,那么这将导致丑陋的错误。但我想不出有什么合理的理由会出现这种情况。有人知道吗?

澄清一下:我知道这是一次黑客攻击,有更安全的解决方案(比如单独的访问器功能)。我只是想知道这是否会因为我丢失了一些信息而立即中断。

这当然是未定义的(EDIT:看起来实际上只是未指定)行为,因为(从语言的角度来看)这两个映射是完全不相关的类型。它现在可能看起来有效,但有时会坏掉,导致大量头痛。

您是否考虑过,可以为类的公共接口提供const_iteratorfind方法,而不是公开实现细节(您在内部使用映射)?

编辑:见5.2.10/7:

指向对象的指针可以是显式转换为指向不同类型的对象。65)除转换类型的右值"指针到T1"到类型"指针到T2"(其中T1和T2是对象路线的类型和位置T2的要求并不严格比T1的那些),并回到其原始类型产生原始指针值,这样的结果未指定指针转换。

从这句话中,我们得出结论,从具有非常量值类型的映射转换为具有常量类型的映射具有未指定的行为。此外,实际取消引用转换后的指针可能会违反严格的别名规则,并导致未定义的行为。

您可以将其作为地图保存<int,常量浮点*>并在需要时在内部进行const_cast。这很难看,但合法(只要你知道所有指向的值实际上都不是const)。

至少它不涉及未定义的行为,我很确定你的解决方案是这样的。尽管正如您所说,它可能在大多数平台上的大部分时间都有效。

该interpret_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);
  }

请注意,您还可以导出返回const_iterator的内容,例如find。

它可能会引起麻烦的一个很好的原因是:即使二进制实现通常是相同的(通常是这样,但谁知道呢),类型仍然不同。一些容器可能使用一些静态字段(或者现在在C++11中使用TLS)(例如,用于优化/调试目的),并且这些字段对于不同的类型必须不同。

想象一下,这样的字段将是一个(null初始化的)指针,它在构造函数中被赋予了一些重要的值(如果还没有赋值的话)。只要没有构造这种类型的对象,就可以安全地假设没有人会尊重它,并且在第一次构造函数调用之后,可以尊重它,而不检查它是否为非null。您的代码可以生成从未构建过的容器,但它的方法与内部指针不同,导致难以跟踪segfault。