这段代码演示了我试图解决的问题:
#include <map>
class Point
{
public:
float m_x;
float m_y;
};
typedef std::set<Point *> PointSet;
typedef std::set<const Point * const> ConstPointSet;
float GetMinimumRange(const ConstPointSet &pointSet)
{
float minimumRange(0.0f);
// find the smallest distance between any pair of points in the set
return minimumRange;
}
float GetMinimumRangeWrong(const PointSet &pointSet)
{
PointSet::iterator first(pointSet.begin());
Point * point(*first);
point->m_x = 42.0f; // I want to prevent this
return 0.0f;
}
class PointSet_
{
public:
std::set<Point *> m_pointSet;
float GetMinumumRange() const
{
PointSet::iterator first(m_pointSet.begin());
Point * point(*first);
point->m_x = 42.0f; // I want to prevent this
return 0.0f;
}
};
void test()
{
PointSet myPointSet;
// Add some points to my set
// This fails because the compiler states it can't convert from PointSet to ConstPointSet.
//float minimumRange1(GetMinimumRange(myPointSet));
// reinterpret_cast<> is the only cast that works here, const_cast fails with the same
// complaint as the line above generates
ConstPointSet *myConstPointSet(reinterpret_cast<ConstPointSet *>(&myPointSet));
float minimumRange1(GetMinimumRange(*myConstPointSet));
float minimumRange2(GetMinimumRangeWrong(myPointSet));
}
我想创建一个需要 PointSet
的例程,计算任意一对 Point
之间的最小范围s 在集合中,但它保证不会修改 PointSet
以任何方式传递给它。它无法修改任何引用的成员 Point
,它不能更改指针本身,也不能从集合中添加或删除成员
问题在于编译器正确查看 PointSet
和ConstPointSet
由于 const
的差异而分为不同类型内部类型的限定符,因此拒绝在它们之间进行转换,即使我只添加 const
预选赛。
我尝试创建一个类来包含 PointSet
,并创建一个 const 成员函数,但即使在那里它也允许修改内部 Point
之一s。至少 MSVC 会毫无怨言地编译它。我承认我对此感到非常惊讶。
我发现有效的唯一方法是使用 reinterpret_cast<>
将指针转换为 PointSet
指向 ConstPointSet
的指针。该标准确实指出 reinterpret_cast<>
可以用来添加const
限定符,但这适用于这种情况吗?
如果没有,有什么办法可以做到我想要的吗?我意识到良好的代码纪律可以用来确保GetMinimumRange()
不修改传递的PointSet
,但我想得到那些 const
那里有预选赛有两个原因。
他们将确保如果有人修改
GetMinimumRange()
他们不能导致它修改PointSet
.它将允许编译器优化对
GetMinimumRange()
的调用。 。在没有const
的情况下限定符,在调用站点不能对可以在调用中缓存的值做出任何假设,因此可能导致数据的冗余获取。
最佳答案
没有直接的方法,因为 const
ness 不通过指针传播。在 const PointSet
,指针本身就是 const
,而不是它们指向的对象。而且,就像您发现的那样,const Point *
与 Point *
是不同的类型,所以std::set<const Point *>
与 std::set<Point *>
是不同的类型.
我不喜欢 reinterpret_cast
STL 结构。这对我来说很可怕。 STL根据模板参数的类型进行各种优化。 std::vector<bool>
是一个极端的例子。你可能会认为std::set<T *>
和std::set<const T *>
会被布局相同,因为它们都是指针,但我不会这么认为,直到我在标准中读到它。
如果它是我自己编写的结构,并且我可以轻松验证 Actor 阵容是否有效,那么它会不那么可怕,但仍然丑陋。
您可以编写一个包装类来保存对 std::set<Point *>
的引用。但只允许const
访问其指向的Points
通过迭代器。如果保证指针是非 null
,您的迭代器可以直接取消引用点。我把它写在这里作为模板:
template <typename T>
class PointerSetViewer
{
public:
PointerSetViewer(std::set<T *> const &set) : set(set) {}
struct iterator : public std::iterator<std::forward_iterator_tag, T const>
{
iterator(typename std::set<T *>::const_iterator it) : it(it) {}
T const &operator*() const { return **it; }
T const *operator->() const { return *it; }
iterator &operator++() { ++it; return *this; }
bool operator==(iterator other) { return it == other.it; }
bool operator!=(iterator other) { return it != other.it; }
private:
typename std::set<T *>::const_iterator it;
};
iterator begin() { return iterator(set.cbegin()); }
iterator end() { return iterator(set.cend()); }
private:
std::set<T *> const &set;
};
它体积庞大,但它可以实现您的目标,而无需做任何冒险的事情:
float GetMinimumRangeWrong(PointerSetViewer<Point> &pointSet)
{
PointerSetViewer<Point>::iterator first(pointSet.begin());
first->m_x = 42.0f; // does not compile
}
此外,如果您使用 C++11,您可以获得一些不错的基于范围的 for
循环:
template <typename T>
PointerSetViewer<T> view_set(std::set<T *> const &set) {
return PointerSetViewer<T>(set);
}
for (Point const &p : view_set(myPointSet)) {
// whatever...
}
巴洛克风格?是的,但是如果一段巴洛克式库代码可以让您编写 100 段漂亮的应用程序代码,并具有更好的类型检查,那么它可能是值得的。
关于c++ - 这是reinterpret_cast 的合法使用吗?如果不是,我该怎么做?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28618592/