12

Say I have a simple class like this

class Foo
{
public:
    void foo()const
    {
        str[5] = 'x';
        obj->changeTheWorld();
        x = 4;
        y.get() = 5;
        obj2->changeTheWorld();
    }
private:
    char *str; //some referenced data, not owned by Foo
    ComplexObj *obj; //some referenced data, not owned by Foo
    int &x; //references as well
    //wrapped reference, but has a "T& get()const"
    std::reference_wrapper<int> y;
    //an occasionally useful pointer wrapper for complex memory cases
    //but has a "T* get()const"
    std::shared_ptr<ComplexObj> obj2;
};

This is valid because in the const method, its just the pointer itself that becomes const, not the data it points to. However in many cases that is not what I desired and I want a compile error if a const method tries to change these members contents (either directly or by calling a non-const method on that member).

Is there a standard solution to this?

I think some kind of wrapper class should be able to achieve this, and should also be something the compiler optimises out, although haven't sat down to try and design such a thing to cover all cases giving say a strong_const<char*> str and strong_const<int&> (also not sure on a good name...).

Fire Lancer
  • 29,364
  • 31
  • 116
  • 182
  • 18
    There is a proposal for C++17, [propagate_const](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4372.html). – M.M Feb 26 '15 at 01:00
  • 1
    [see here](http://stackoverflow.com/questions/28618592/is-this-a-legitimate-use-of-reinterpret-cast-and-if-not-how-do-i-do-this/) or [here](http://stackoverflow.com/questions/4729820/propagate-constness-to-data-pointed-by-member-variables) for some options – M.M Feb 26 '15 at 01:04
  • `std::string` would behave the way you want. Not sure why the reference would compile, that seems wrong to me. – Mark Ransom Feb 26 '15 at 01:04
  • @MarkRansom classes can have reference members. (It makes the implicitly-generated constructors be `delete`d) – M.M Feb 26 '15 at 01:07
  • @MattMcNabb I wasn't questioning the existence of reference members, just the ability to store to them in a `const` member function. – Mark Ransom Feb 26 '15 at 02:18
  • 1
    Showed some non-char cases. My intention was never to consider that as a string, just as something external to the type and not owned by it (so cant be a value type because that's a copy, and unique_ptr, shared_ptr etc have the same const behaviour as the raw pointer). – Fire Lancer Feb 26 '15 at 09:56

1 Answers1

4

Well, neither std::reference_wrapper nor std::shared_ptr do not provide const propagation, so they are not more "const-strict" than regular pointer.

I'd recommend to make your own const propagation class (I am not sure - maybe something similar is already provided by boost - please let me know in comments)

My proposition is this class:

#include <memory> // for pointer_traits

template <typename Pointer>
class ConstPropagatePointer 
{
public:
    using element_type = typename std::pointer_traits<Pointer>::element_type;
    using pointer = typename std::pointer_traits<Pointer>::pointer;
    using const_pointer = element_type const * const;
    using reference = element_type&;
    using const_reference = element_type const&;

    ConstPropagatePointer(Pointer ptr) : ptr(ptr) {}
    pointer operator -> ()
    {
        return &(*ptr);
    }
    const_pointer operator -> () const
    {
        return &(*ptr);
    }
    reference operator * ()
    {
        return *ptr;
    }
    const_reference operator * () const 
    {
        return *ptr;
    }
private:
    Pointer ptr;
};

So that will work for you:

class Foo
{
public:
private:
    ConstPropagatedPointer<char*> str; 
    ConstPropagatedPointer<ComplexObj*> obj; 
    ConstPropagatedPointer<std::shared_ptr<ComplexObj>> obj2;
};
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112