我有一个带有std ::指针映射的类作为成员。现在,我想以只读方式公开该成员:既不允许修改地图,也不允许修改指向的对象。在内部我需要那些非const的指针,我想将它们公开为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指针映射是不同的,那么这将导致丑陋的错误。但我想不出任何理智的原因。有人有任何想法吗?
澄清:我知道这是一个黑客,并且有更安全的解决方案(如单独的访问者功能)。我只是想知道这是否会因为我遗漏的一些信息而立即中断。
答案 0 :(得分:9)
这当然是未定义的(编辑:它看起来实际上只是未指定的)行为,因为这两个地图(从语言的角度来看)完全不相关的类型。它似乎现在可以工作,但有时它会破裂并导致大量的麻烦。
您是否考虑过而不是公开实施细节(您在内部使用地图),而是可以为您班级的公共界面提供const_iterator
和find
方法?
编辑: 见5.2.10 / 7:
指向对象的指针可以是 显式转换为指向 不同类型的对象。 65)除外 转换类型的右值 “指向T1的指针”到“指针”类型 到T2“(其中T1和T2是对象 类型和对齐的位置 T2的要求不严格 比那些T1)并回到它的 原始类型产生原始 指针值,这样的结果 指针转换未指定。
从该引用中我们得出结论,从具有非const值类型的映射到具有const值类型的映射具有未指定的行为。此外,实际取消引用转换后的指针可能会违反严格的别名规则并导致未定义的行为。
答案 1 :(得分:1)
您可以将其保存为地图&lt; int,const float *&gt;你需要的时候在内部和const_cast。这是丑陋但合法的(只要你知道指向的所有值都不是const)。
至少它不涉及未定义的行为,我非常肯定你的解决方案。虽然如你所说,它可能在大多数平台上大部分时间都有效。
答案 2 :(得分:0)
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之类的内容。
答案 3 :(得分:0)
它可能导致麻烦的一个很好的理由:即使二进制实现通常是相同的(通常是,但谁知道),那么类型仍然是不同的。某些容器可能会使用一些静态(或现在在C ++ 11中的TLS)字段(例如,用于优化/调试目的),并且对于不同类型的那些容器必须是不同的。
想象一下,这样的字段将是一个(空初始化的)指针,它在构造函数中给出了一些重要的值(如果尚未分配)。只要没有构造这种类型的对象,就可以安全地假设没有人会遵循它,并且在第一次构造函数调用之后,可以在不检查它是否为非null的情况下对其进行处理。你的代码可以生成从未构造过的容器,但它的方法是deference内部指针,导致很难跟踪段错误。