【问题标题】:How to expose a map of pointers as a map of const pointers?如何将指针映射公开为 const 指针映射?
【发布时间】:2011-09-26 04:30:31
【问题描述】:

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

澄清一下:我知道这是一个 hack,并且有更安全的解决方案(例如单独的访问器函数)。我只是想知道这是否会因为我缺少一些信息而立即中断。

【问题讨论】:

  • 很遗憾,自动向下转换为 const 的 C 风格规则并没有跟上现代 C++ 编程的步伐。
  • 你可以使用一个类指针对象的映射(又名“智能指针”)来传播常量。

标签: c++ pointers stl map constants


【解决方案1】:

这当然是未定义的(编辑:看起来它实际上只是未指定的)行为,因为这两个映射(从语言的角度来看)是完全不相关的类型。它现在似乎可以工作,但有时它会崩溃并引起大量头痛。

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

编辑: 见 5.2.10/7:

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

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

【讨论】:

  • +1,隐藏实现是最好的方法,即使看起来工作量更大。
  • 一般来说你是对的,但是......我看不出容器的 const 指针和非 const 指针版本有什么不同的任何原因。你能告诉我一个合理的理由吗?
  • 或者一般来说,您可以查看 Sorted Associative Container 接口,划掉任何需要容器为非常量的内容,然后决定要实现多少剩余部分。然后编写一个类来适应非常量指针的映射并实现您决定的所有内容。
  • @Zed 标准说它们可以不同,所以我不想推测编译器在有这个余地的情况下会做什么。
  • @Mark B 真的吗?我认为标准没有提到...(如果我错了,请随时纠正我。:-))这更像是一种给定的可能性,因为它是一个模板类。我只是看不出有任何理由。
【解决方案2】:

您可以在需要时将其作为 map 和 const_cast 在内部保存。这是丑陋但合法的(只要您知道所有指向的值实际上都不是 const)。

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 它没有复制,使用 const_iterators 我仍然可以修改指向的对象。
    • 啊,是的,你没有复制。您正在使用未指定的行为进行引用。关于修改指向的对象:如果不使用 C 样式转换或 const_cast,您将无法做到这一点。许多项目都将 const_casts 禁止(您需要弃权)。大多数项目都进行 C 风格的演员表和 reinterpret_cast 完全禁止(项目经理或项目死亡时授予豁免,以较晚者为准)。
    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-11
      • 1970-01-01
      相关资源
      最近更新 更多