c++ - 这是reinterpret_cast 的合法使用吗?如果不是,我该怎么做?

标签 c++ pointers constants reinterpret-cast

这段代码演示了我试图解决的问题:

#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 ,它不能更改指针本身,也不能从集合中添加或删除成员

问题在于编译器正确查看 PointSetConstPointSet由于 const 的差异而分为不同类型内部类型的限定符,因此拒绝在它们之间进行转换,即使我只添加 const预选赛。

我尝试创建一个类来包含 PointSet ,并创建一个 const 成员函数,但即使在那里它也允许修改内部 Point 之一s。至少 MSVC 会毫无怨言地编译它。我承认我对此感到非常惊讶。

我发现有效的唯一方法是使用 reinterpret_cast<>将指针转换为 PointSet指向 ConstPointSet 的指针。该标准确实指出 reinterpret_cast<>可以用来添加const限定符,但这适用于这种情况吗?

如果没有,有什么办法可以做到我想要的吗?我意识到良好的代码纪律可以用来确保GetMinimumRange()不修改传递的PointSet ,但我想得到那些 const那里有预选赛有两个原因。

  1. 他们将确保如果有人修改 GetMinimumRange()他们不能导致它修改 PointSet .

  2. 它将允许编译器优化对 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/

相关文章:

c++ - 如何自动生成c/c++函数原型(prototype)?

C 编程链表指针返回奇怪的值

c - 为什么 char[] 在堆栈上,而 char * 在堆上?

c++ - 将字符串变量与一组字符串常量进行比较的最佳方法是什么?

c++最终常量字符串

swift - 更改表格 View 高度后,CellForRowAt 不会调用

c++ - 从静态对象的内部类调用外部类虚函数

c# - C# 中不保留 C++ 函数中的数组更改

C++ 将指针传递给带有引用参数的函数

c++ - QByteArray 删除前 4 行