template <class T>
class deep_const_ptr
{
    T * priv;
public:
    deep_const_ptr(const deep_const_ptr & p_other); // copy ctor

    T const * operator->() const;
    T * operator->();
    ..... // other crap
};

void Cheese::doThings(const deep_const_ptr<Cheese> p_other)
{
    deep_const_ptr<Cheese> awayGoesTheConst(p_other); // I don't want this to work.
    awayGoesTheConst->doTerribleThings();
}

I'd like to block copy construction from a const to a non-const.

Maybe this doesn't even make any sense?

Might this be possible with the new C++11 / 14 / 17?

EDIT:

Yes, the issue is that if I don't give the copy constructor, STL containers wont work. And I'd quite need them to work.

EDIT:

deep_const_ptr was the solution what I came up with when faced with the problem of STL containers and const objects.

class Shape
{
    private:
        std::list<Vertex *> m_vertices;
    public:
        virtual void std::list<Vertex*> * const getVertices() { return &m_vertices; }
        virtual void std::list<Vertex const *> * const getVertices() const { return &m_vertices; } // doesn't work, cannot convert between different types of lists
}

I want to provide Immutable Vertex objects from an Immutable Shape.

The two solutions I came up with were custom deep_const_ptr pointer types and custom lists where

Vertex const *& operator[](unsigned int) const;
有帮助吗?

解决方案

I assume what you are after is a deep-const-pointer implementation and what to do with its copying. As you already found out, a simple copy constructor will not do, since you will just lose the const on your pointer. There is no other way to disallow the const removal on the outer object, so you just need to disable the copy constructor. Now the current version of your question does not make it clear why that is a problem. From your comments and the previous question, I can only infer that you want to use the pointer in containers, std::vector<> specifically. The key to making that work is to disable copy, but enable move (Note that you need rvalue-reference support for this, which is part of C++11):

template <class T>
class deep_const_ptr
{
    T * priv;
public:
    deep_const_ptr(const deep_const_ptr &)  = delete; // Do not allow this!
    deep_const_ptr& operator=(const deep_const_ptr &) = delete; // Nor this!

    deep_const_ptr(deep_const_ptr &&) = default; // But this is okay
    deep_const_ptr& operator=(deep_const_ptr &&) = default; // So is this!

    T const * operator->() const;
    T * operator->();
    ..... // other crap
};

Containers typically only need move, not copy, to work.

If you really need support for the copy, you need to do it to a different type that only supports the const access. For example, you could specialize the deep_const_ptr for const T and only implement the const variant of operator-> and operator* in there. Additionally, you'd allow "demotion" of a deep_const_ptr to a deep_const_ptr via the conversion operator. But this implies some sort of multiple owner handling, such as reference counting, so I'm going to leave it out. It's also weird that the const and non-const version behave differently, but it's your decision. Personally, I'd recommend just sticking with the move.

其他提示

Edit Edit: You updated your answer again, so let me reply to that.

Just use a const pointer:

void Cheese::doThings(const Cheese* p_other)
{
    Cheese* p = p_other; // compiler ERROR
    p->doTerribleThings(); // can not do terrible things
}

Or if you want a shared_ptr:

void Cheese::doThings(const std::shared_ptr<const Cheese>& p_other)
{
    std::shared_ptr<Cheese> p = p_other; // compiler ERROR
    p->doTerribleThings(); // can not do terrible things
}

const ptr<T> is not the same as ptr<const T>. The first indicates a pointer object which can not be changed, while the object pointed to can. The second indicates a pointer object which can be changed, but the object pointed to can not.

For a function signature void foo(const A a) does not make much sense. You say "give me a copy, and I promise to not change it". Who cares what you do with your copy? Use void foo(const A& a) to avoid creating a copy.

Edit:

I'd like to allow copying nonconst -> nonconst, nonconst -> const, const -> const.

This is already given for a "normal" pointer T* or an std::shared_ptr.

Old:

Just use T* operator->() const;. If the stored pointer will be a non-const pointer like Cheese* it can be converted to a const Cheese* automatically.

The function operator->() is marked as const, as it does not change the pointer object itself. This has nothing to do with the stored object!

Example with vector:

std::vector<Cheese*> a(10);
a[0] = 0; // does change a
Cheese* p = a[0]; // does not change a
a[0]->foo(); // still does not change a!

If you want objects pointed to by deep_const_ptr to be const, you have to use deep_const_ptr<const Cheese>.

Example with vector:

std::vector<const Cheese*> a(10); // const pointer!
a[0] = 0; // does change a 
const Cheese* p = a[0]; // does not change a
a[0]->foo(); // now this only works if Cheese::foo is const
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top