const_cast的这种用法是否在实践中未定义?

时间:2016-05-06 15:16:20

标签: c++ const

我有一个管理输入的类。要显示和更改键绑定,在调用者完成之前将其提交给管理器之前,为其提供可以拥有和更改的绑定的映射非常重要。但是,在此映射中可以插入/删除的内容中只有管理员知道的内容有特定的规则,因此必须强制调用者要求管理员进行更改。

来电者获取地图的const版本以确保无法在其上修改地图,而经理仍然可以使用const_cast

更改地图
typedef std::multimap<Key, Input> Map;

class InputManager{
    public:

    const Map getBindings(){
        // builds the map and returns it
    }

    bool insertBinding(const Map & bindings, Key key, Input input){
        // performs the insert after checking several rules first
        if(rulesAllowIt){
            const_cast<Map&>(bindings).insert(std::make_pair(key, input));
            return true;
        }else{
            return false;
        }
    }

    void commitBindings(const Map & bindings){
        // commits the bindings to replace the old
    }
}

这目前按预期工作,但我担心使用const_cast,因为原则上修改const变量是UB(除非有例外?)

在实践中,这是否会导致UB或其他平台或编译器上的任何其他微妙的性能问题或错误,或与可能的优化冲突?

2 个答案:

答案 0 :(得分:2)

这看起来有点像code smell。虽然它会起作用但事实是你已经宣布你的insertBindings方法采用一个恒定的地图,但你并没有遵守方法中的常数。

如果您可能会修改地图,那么只需将该类型声明为非const,以便该方法的行为方式符合每个人的预期。

答案 1 :(得分:2)

const Map getBindings()是一个相当无意义的返回类型。用户可以在任何情况下编写代码:

Map foo = manager.getBindings();
manager.insertBinding(foo, key, whatever);
foo.clear();
manager.commitBindings(foo);

在这种情况下,代码已经定义了行为(至少,直到commitBindings可能不期望看到空地图的点)。或者他们可以这样写:

const Map foo = manager.getBindings();
manager.insertBinding(foo, key, whatever);
manager.commitBindings(foo);

在这种情况下,代码按标准具有UB。 在实践中失败的一种方法是优化器有权假设,因为fooconst - 在定义它的位置是合格的,那么其非mutable数据成员的值不会更改。因此,在内联commitBindings的代码后,它可以重新排序对foo的访问权限,以便在调用insertBinding之前读取数据成员在它被修改。

在实践中,您的Map似乎不会发生这种情况,但修改const - 限定对象的一个​​原因可能不仅仅是一个理论问题

要获得您想要的位置,您可以添加另一级别的间接(编译时间接:编译器可以消除开销)。定义一个具有Map作为私有数据成员的类,其唯一的公共mutator与insertBinding具有相同的效果。然后,除了通过检查规则的代码之外,用户无法修改Map。为了提高效率,请确保您的类具有工作移动构造函数并移动赋值,因为复制std::multimap可能需要做很多工作。