这是对reinterpret_cast的合法使用吗?如果不是,我该怎么做? [英] Is this a legitimate use of reinterpret_cast and if not how do I do this?

查看:107
本文介绍了这是对reinterpret_cast的合法使用吗?如果不是,我该怎么做?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此代码演示了我要解决的问题:

This code demonstrates the problem I'm trying to solve:

#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之间的最小范围,但它保证不会修改传递给它的PointSet任何方式都没有.它不能修改任何引用的Point的成员,不能更改指针本身,也不能从集合中添加或删除成员

I want to create a routine that takes a PointSet, evaluates the minimum range between any pair of Points in the set, but that it guarantees that it won't modify the PointSet passed to it in any way at all. It can't modify the members of any referenced Point, it can't change the pointers themselves, nor can it add or remove members from the set

问题在于,由于内部类型的const限定符的不同,编译器正确地将PointSetConstPointSet视为不同的类型,因此即使我只是添加,也拒绝在它们之间进行强制转换const限定词.

The issue is that the compiler correctly views PointSet and ConstPointSet as different types because of the difference of const qualifiers of the inner type, and therefore refuses to cast between them, even though I'm only adding const qualifiers.

我试图创建一个包含PointSet的类,并创建一个const成员函数,但是即使在其中,它也允许对内部之一进行修改.至少MSVC会毫无保留地进行编译.我承认我对此感到很惊讶.

I tried creating a class to contain a PointSet, and creating a const member function, but even in there it allows modification to one of the inner Points. At least MSVC will compile that without complaint. I'll confess I was quite surprised about this.

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

The only way I've found that works is to use a reinterpret_cast<> to convert a pointer to a PointSet to a pointer to a ConstPointSet. The standard does note that reinterpret_cast<> can be used to add const qualifiers, but does that apply in this case?

如果没有,有什么办法可以做我想要的吗?我意识到可以使用良好的代码规范来确保GetMinimumRange()不会修改传递的PointSet,但是出于两个原因,我想在那里获得那些const限定符.

If not, is there any way to do what I want? I realize that good code discipline can be used to ensure that GetMinimumRange() doesn't modify the passed PointSet, but I'd like to get those const qualifiers in there for two reasons.

  1. 他们将确保如果有人修改了GetMinimumRange(),则不会导致其修改PointSet.

  1. They will ensure that if anyone ever modifies GetMinimumRange() they can't cause it to modify the PointSet.

它将允许编译器优化对GetMinimumRange()的调用.在没有const限定符的情况下,无法在调用站点上对可能在整个调用中缓存的值进行任何假设,从而可能导致冗余的数据提取.

It will allow the compiler to optimize over the call to GetMinimumRange(). In the absence of the const qualifiers, no assumptions can be made at the calling site regarding values that could be cached across the call, thus possibly leading to redundant fetches of data.

推荐答案

没有直接的方法,因为const ness不会通过指针传播.在const PointSet中,指针本身是const,而不是它们指向的对象.而且,就像您发现的那样,const Point *是与Point *不同的类型,所以std::set<const Point *>是与std::set<Point *>不同的类型.

There is no straightforward way, because constness does not propagate through pointers. In a const PointSet, it's the pointers themselves that are const, not the objects they point to. And, like you've discovered, const Point * is a different type from Point *, so std::set<const Point *> is a different type from std::set<Point *>.

我不喜欢STL结构的reinterpret_cast.这让我很害怕. STL根据模板参数的类型进行各种优化. std::vector<bool>是一个极端的例子.您可能会认为std::set<T *>std::set<const T *>的布局相同,因为它们都是指针,但是直到我在Standard中阅读它之前,我都不会这样假设.

I don't like the reinterpret_cast of a STL structure. That is scary to me. STL does all kinds of optimizations based on the type of template parameters. std::vector<bool> being an extreme example. You'd think that std::set<T *> and std::set<const T *> would be laid out the same because they are both pointers, but I wouldn't assume so until I read it in the Standard.

如果这是我自己编写的结构,并且我可以轻松地验证演员表是否可以正常工作,那么它虽然不那么恐怖,但仍然很丑陋.

If it were a structure I had written myself, and I could easily verify that the cast would work, it would be less scary but still ugly.

您可以编写一个包装类,该包装类包含对std::set<Point *>的引用,但仅允许const通过迭代器访问其指向的Points.如果保证指针不是-null,则迭代器可以直接取消引用这些点.我已经将其作为模板写在这里:

You could write a wrapper class that holds a reference to a std::set<Point *> but only allows const access to its pointed-to Points via iterators. If the pointers are guaranteed to be non-null, your iterator can dereference the points directly. I've written it here as a template:

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;
};

体积大,但可以轻松完成目标:

It's bulky, but it accomplishes your goals without doing anything risky:

float GetMinimumRangeWrong(PointerSetViewer<Point> &pointSet)
{
    PointerSetViewer<Point>::iterator first(pointSet.begin());
    first->m_x = 42.0f;            // does not compile
}

另外,如果您使用的是C ++ 11,则可以获得一些基于范围的for循环:

Also if you're using C++11, you can get some nice range-based for loops:

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条漂亮的应用程序代码,并进行更好的类型检查,那可能是值得的.

Baroque? Yes, but if one piece of baroque library code lets you write 100 pieces of beautiful application code with better type checking, it's probably worth it.

这篇关于这是对reinterpret_cast的合法使用吗?如果不是,我该怎么做?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆