我刚刚发现了一些对我来说像是怪癖的东西
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&
构造函数参数的原因,我不知道如何从Map const
构造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
字段,不是吗?
为什么编译器会抱怨最后一个(第一个非常合乎逻辑),我可以做一些事情来重写整个Tile类专门用于const
访问吗?它可能是const_cast
正确的神话之地吗?
提前谢谢。
Daniel的做法是正确的-您肯定需要一个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
我认为没有办法创建Tile类的两个版本:一个用于常量访问,另一个用于可变访问。考虑以下问题:如果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或constMap,具体取决于实例化。