BWG
BWG

Reputation: 2278

Returning indirect reference to self in const function

I'm learning to despise const.

struct b;

struct a {
    b* p;
    void nonConst() { p = nullptr;}
    b* getP() const { return p;}
};
struct b {
    a *p;
    a *getP() const { return p;}
};

void func (const a *ii) {
    b *uRef = ii->getP();

    //dear god, we've just obtained a pointer to a non-const a, starting with const a
    a *iii = uRef->getP();

    //and we've just modified ii, a const object
    iii->nonConst();
}

int main() {
    a *i = new a;
    b *u = new b;
    i->p = u;
    u->p = i;

    func(i);
}

Could this induce any undefined behavior? If not, is it breaking any const rules? If not that, why is this treated as a const pointer to const data, and not just a const pointer to non-const data?

Upvotes: 0

Views: 357

Answers (1)

M.M
M.M

Reputation: 141554

There are no const variables in your code, so there is no UB.

In void func (const a *ii), the const means ii might either be pointing to an a or a const a, so we shouldn't allow writing through this pointer in case it actually points to a const a.

But in your code it doesn't actually point to a const a.


The underlying issue you seem to be having is related to the basic structure:

struct C
{
    std::string *ptr;
};

In this case, if we have a variable:

const C c = something;

then the question is, should it be permitted to write:

*(c.ptr) = "Hello";

The rules of C++ say "Yes, this is fine". This is because c.ptr is const, but the thing being pointed to is not const.

The question of whether or not this is actually a good idea is something that you have to decide on as part of your class interface (which you will describe in your class's documentation).

If the value of the string is supposed to be an invariant for instances of C, then you may want to disallow this, or at least, discourage it.

Back to your original code, if you want a const a to represent holding a const b, then you would need to put:

b const * getP() const { return p;}
  ^^^^^

and then the caller will not be able to write b *uRef = ii->getP(); , they'd have to at least throw in a const_cast.

Finally, there isn't an easy way for a const a to actually hold a b const *, but a non-const a to hold a b *. A simpler solution is to actually have two different classes here (say a and const_a).

Upvotes: 2

Related Questions