我有一个管理输入的类。要显示和更改键绑定,重要的是给调用者一个它可以拥有和更改的绑定的映射,然后再将其提交给管理器。但是,只有管理员可以知道在此映射中可以插入/删除哪些内容的特定规则,因此必须强制呼叫者要求管理员进行更改。
呼叫者会获得地图的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或任何其他细微的性能问题或在不同平台或编译器上的错误,或与可能的优化冲突?
最佳答案
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。它可能在实践中失败的一种方式是,优化器有权假定由于
foo
在定义点处是const
限定的,因此其非mutable
数据成员的值将没变。因此,在内联commitBindings
的代码之后,它可以重新排序对foo
的访问,以便在对insertBinding
的调用导致其被修改之前读取数据成员。在实践中,您的
Map
似乎并没有发生这种情况,但这是为什么修改const
限定对象的原因不仅仅是理论上的问题,而且是原因之一。为了达到您想要的位置,您可以添加另一个级别的间接寻址(编译时间接寻址:编译器可以消除开销)。将具有
Map
的类定义为私有数据成员,并且其唯一的公共mutator与insertBinding
具有相同的作用。这样,用户只能通过检查规则的代码来修改Map
。为了提高效率,请确保您的类具有有效的move构造函数和move分配,因为复制std::multimap
可能需要很多工作。关于c++ - 实际上,const_cast的这种用法是否未定义?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37075913/