我有一个管理输入的类。要显示和更改键绑定,在调用者完成之前将其提交给管理器之前,为其提供可以拥有和更改的绑定的映射非常重要。但是,在此映射中可以插入/删除的内容中只有管理员知道的内容有特定的规则,因此必须强制调用者要求管理员进行更改。
来电者获取地图的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或其他平台或编译器上的任何其他微妙的性能问题或错误,或与可能的优化冲突?
答案 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。 在实践中失败的一种方法是优化器有权假设,因为foo
是const
- 在定义它的位置是合格的,那么其非mutable
数据成员的值不会更改。因此,在内联commitBindings
的代码后,它可以重新排序对foo
的访问权限,以便在调用insertBinding
之前读取数据成员在它被修改。
在实践中,您的Map
似乎不会发生这种情况,但修改const
- 限定对象的一个原因可能不仅仅是一个理论问题
要获得您想要的位置,您可以添加另一级别的间接(编译时间接:编译器可以消除开销)。定义一个具有Map
作为私有数据成员的类,其唯一的公共mutator与insertBinding
具有相同的效果。然后,除了通过检查规则的代码之外,用户无法修改Map
。为了提高效率,请确保您的类具有工作移动构造函数并移动赋值,因为复制std::multimap
可能需要做很多工作。