这是一个有趣的问题。您希望能够在推理时保护这些点,但在推理完它们后返回对它们的可变引用。
作为对这些点进行推理的结果,您实际上返回的是它们的“名称”或“标识符”。
可以想象,我们可以按名称映射它们,并sieve()
返回相关名称的向量。如果我们想避免存储正式名称(唯一编号、文本字符串等)的开销,这样的名称可以只是一个地址。
如果我们使用 const 对象的地址作为名称,那么当然要将其转回对可变对象的引用,我们需要一个const_cast
. 这可能被视为我们可以合理使用 const 强制转换的少数几次之一。这样做时,我们应该将其封装在实用程序类中以限制任何后果。
编辑:重新考虑解决方案。这个现在不能被不守规矩的客户端代码滥用。
#include <iostream>
#include <vector>
struct point { int x, y; };
inline std::ostream& operator<<(std::ostream& os, const point& p)
{
os << "(" << p.x << ", " << p.y << " )";
return os;
}
struct point_collection
{
using container_type = std::vector<point>;
point_collection(container_type points) : _points(std::move(points)) {}
using const_iterator = const point*;
using iterator = point*;
const_iterator begin() const { return &*_points.begin(); }
const_iterator end() const { return &*_points.end(); }
// note that this function is only available on a non-const point_collection
point& mutable_reference(const_iterator input)
{
// could put an assert in here to ensure that input is within our range
return *const_cast<iterator>(input);
}
container_type _points;
};
std::vector<point_collection::const_iterator> sieve(point_collection::const_iterator first,
point_collection::const_iterator last)
{
std::vector<point_collection::const_iterator> result;
for ( ; first != last ; ++first )
{
if (first->x > 6)
result.push_back(first);
}
return result;
}
point_collection make_a_universe()
{
return {
std::vector<point> {
{ 10, 10 },
{ 6, 6 }
}
};
}
auto main() -> int
{
using namespace std;
auto universe = make_a_universe();
auto interesting = sieve(universe.begin(), universe.end());
for (auto point_id : interesting) {
auto& p = universe.mutable_reference(point_id);
p.x = -p.x;
cout << p << endl;
}
return 0;
}
预期输出:
(-10, 10 )