3

Take this dummy structure:

struct foo
{
    int* pI;
    void bar() const
    {
        int* ptrToI = pI; // I want this to require 'const int*'
        *ptrToI = 5; // I want this to fail
    }
}__test__;

How can I design this struct to prevent me from changing the value pI points to?

KungPhoo
  • 516
  • 4
  • 18
  • 7
    `const int* pI;` ? What's the problem exactly? – StoryTeller - Unslander Monica Dec 21 '17 at 09:32
  • 2
    Unrelated to your problem, but don't use symbols with two leading underscores (like e.g. `__test__`), those are reserved in all scopes. Please see [this old question and its answers for more information](http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier) – Some programmer dude Dec 21 '17 at 09:33
  • 1
    The solution was proposed to be included in the C++ standard: [propagate_const](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4388.html) – Oliv Dec 21 '17 at 09:52
  • 1
    If you don't need `foo` to be assignable, and don't need to reset what a `pI` refers to, then you *can*, technically, simply use a C++ reference instead of a C++ pointer. Otherwise you might use a custom smart pointer, as @lopital suggests in an answer. But chances are that you are re-inventing something like `std::string` or `std::vector`, and then, use the standard container. – Cheers and hth. - Alf Dec 21 '17 at 09:57

2 Answers2

1

Unless you can make the member const int*, you could use inheritance to hide the member from what becomes the child class, and provide a protected function in the base class that yields a const int* pointer:

class base_foo
{
    int* pI;
protected:
    const int* get_pI(){return pI;}
};

struct foo : base_foo
{
    int* ptrToI = get_pI(); // will fail due to attempted conversion to int*
    /* and so on*/

Note also that any token containing two consecutive underscores is reserved and as such, formally, the behaviour of your program is undefined: rename __test__.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
1

You can use a custom smart pointer in order to hide the underlying pointer:

template <typename T>
struct ptr_t
{
private:
    T *ptr;
public:
    //Constructors and assignment operators
    ptr_t(): ptr(nullptr) {}
    ptr_t(T* p): ptr(p) {}
    ptr_t(const ptr_t &other): ptr(other.ptr) {}
    ptr_t& operator=(T* p) {this->ptr=p;return *this;}
    ptr_t& operator=(const ptr_t &other) {this->ptr=other.ptr; return *this;}

    //Note that the smart pointers included in the standard returns non-const pointers
    //That is why you need to define a custom smart pointer, which forces the const pointer return in the const version of the operators.
    const T* operator->() const {return ptr;}
    T* operator->() {return ptr;}
    const T& operator&() const {return *ptr;}
    T& operator&() {return *ptr;}

    operator const T*() const {return ptr;}
    operator T*() {return ptr;}
};

struct foo2
{
    ptr_t<int> pI;
    void nonconst_bar()
    {
        int* ptrToI = pI; // Now success, since not const
        *ptrToI = 5;
    }

    void failing_bar() const
    {
        //int* ptrToI = pI; // This fails
        //*pI = 5; // This also fails
    }

    void success_bar() const
    {
        const int* ptrToI = pI;
        //*ptrToI = 5; // This is not possible anymore
    }
};
LoPiTaL
  • 2,495
  • 16
  • 23