我刚刚发现了一些看起来像是怪癖的东西。考虑一下:
struct Tile {
Tile(Map &map, int, int)
: map(map) { }
void destroy();
void display() const;
Map ↦
};
这个(精简版)类是一个访问者对象。它由Map
本身构建:
Tile Map::operator ()(int x, int y) {
return Tile(*this, x, y);
}
Tile const Map::operator ()(int x, int y) const {
return Tile(*this, x, y);
}
因此Map
可以返回Tile
,我们可以从中调用destroy()
(更新地图),而Map const
只能返回Tile const
我们只能从中调用非修改display()
方法。
所以一切都很好,对吧?嗯,不太好。因为尽管起初看起来很简单,但由于Map const
构造函数参数,我无法弄清楚如何从Map&
构造Tile。
我也尝试删除Tile
的构造函数并对其进行聚合初始化,但无济于事:
Tile const Map::operator ()(int x, int y) const {
return { *this, x, y };
}
...这对我来说更加陌生,因为我得到了......
error: invalid initialization of reference of type ‘Map&’ from expression of type ‘const Map’
...即使Tile const
只应包含const
个字段,不应该吗?
为什么编译器抱怨最后一个(第一个是非常合乎逻辑的),我可以做一些专门为const
访问重写整个Tile类的事情吗?它可能是const_cast
正确的神话之地吗?
提前致谢。
答案 0 :(得分:3)
丹尼尔走在正确的轨道上 - 你肯定需要一个Tile
和ConstTile
类,为了简单起见可以模板化,但你需要处理什么时候可以调用destroy()
以及如何你可以构建它们。为此:
template<class MapT>
struct TileT {
TileT(MapT &map, int, int)
: map(map) { }
// we want to be able to construct ConstTile from Tile
template <typename M>
TileT(const TileT<M>& tile)
: map(tile.map) { }
void destroy() {
static_assert(!std::is_const<MapT>::value, "Cannot call destory() from ConstTile");
// rest of implementation
}
void display() const;
MapT ↦
};
using Tile = TileT<Map>;
using ConstTile = TileT<const Map>;
这将为您提供所需的功能,其工作方式与iterator
/ const_iterator
的工作方式类似。所以你可以这样做:
Map map;
...
ConstTile tile = map(4,3); // non-const map, ConstTile is ok
答案 1 :(得分:2)
我认为没有办法创建两个版本的Tile类:一个用于const访问,一个用于可变访问。请考虑以下内容:如果Map引用是const,那么destroy函数应该做什么?
如果您想要创建类的Tile和ConstTile版本,可以使用模板来实现相同的效果,并且仍然可以避免代码重复。
template<class MapT>
struct Tile {
Tile(MapT &map, int, int)
: map(map) { }
template<typename U = MapT>
std::enable_if<std::is_const<U>::value> destroy();
void display() const;
MapT ↦
};
MapT现在可以是Map或const Map,具体取决于实例化。