Is it UB to call a non-const method on const instance when the method does not modify members? [duplicate]
Asked Answered
K

2

6

Code speaks more than thousand words, so...

This is undefined behaviour for mutating a const int:

struct foo {
    int x;
    void modify() { x = 3; }
};

void test_foo(const foo& f) {
    const_cast<foo&>(f).modify();
}

int main(){
    const foo f{2};
    test_foo(f);
}

What about this:

struct bar {
    void no_modify() { }
};

void test_bar(const bar& b) {
    const_cast<bar&>(b).no_modify();
}

int main(){
    const bar b;
    test_bar(b);
}

Is it allowed to call a non-const method on a const object (via const_cast) when the method does not mutate the object?

PS: I know that no_modify should have been declared as const and then the question is pointless, but assume bars definition cannot change.

PPS: Just do be sure: Dont do this at home (or anywhere else). I'd never let such code pass a review. The cast can be avoided trivially. This is a language-lawyer question.

Kat answered 1/10, 2019 at 15:13 Comment(5)
Should be UB at the point of calling the method, but I do not have the energy to dig the standard right now for the proper wording.Cordwain
@Cordwain thats what I was hoping for, maybe I'll have some time for digging myself laterKat
Bar the empty base, it looks similar to this question #47919869Hypnoanalysis
@StoryTeller actually yes. Your answer there could also apply here, so I'd be fine with flagging as duplicateKat
There is dup but without accepted answerDriftage
T
4

The behaviour of your code is well-defined.

The only thing that matters is if you actually modify the object. You don't. All you do is call a member function, and that function does not modify the object.

Tadashi answered 1/10, 2019 at 15:20 Comment(11)
Do note this is tagged as a language lawyer question.Marrowfat
@NathanOliver: I know - hopefully someone will give a standardese answer. I'm merely giving the correct one ;-)Tadashi
This might be a tough one to quote the standard for. I suspect this is legal simply because it doesn't do anything illegal and not because something in the standard explicitly allows this. So you'd probably need to justify every questionable part of the code individually with it's own standard quote.Leeuwarden
the only part in the stadnard I found so far just says that I cannot call b.no_modify() which is kinda naturalKat
@FrançoisAndrieux yes it is hard to prove the absence of something, though I'll be happy already with a "nothing in the standard disallows it" until someone can disprove thatKat
This answer doesn't strike as an agreeable one. Assume modifiable no_modify, which is in it's right to be, as it is not marked const. Now, if you call this through const_cast, the behavior gotta be undefined. It would extremely unfair to put the undefined behavior onus on the innocent no_modify, which played by the book. The undefined behavior has to be in the calling code, which leads us to calling non-const method.Cordwain
@SergeyA: It's extremely relevant. The function does not modify the object. And a member function does not comprise the state of an object.Tadashi
Ok, may be my reasoning is wrong. It actually should be the same as with non-member function accepting non-const reference. I take my statement back.Cordwain
@SergeyA: Absolutely!Tadashi
@Cordwain thats a nice analogy. I would not have asked the question for a free_function_non_modify(bar&) that never modifies the parameterKat
@formerlyknownas_463035818: Indeed - everyone gets excited about member functions for some reason. They are only really global functions with an implicit this; const or otherwise. I avoided mentioning this in my answer since virtual causes an extra headache or two.Tadashi
A
1

In the C++14 standard N4296 that I have access to we see a note in 5.2.11/6:

[ Note: Depending on the type of the object, a write operation through the pointer, lvalue or pointer to data member resulting from a const_cast that casts away a const-qualifier74 may produce undefined behavior (7.1.6.1). —end note ]

Technically I suspect the note may not be normative but it seems clear that the intention here is that casting away const-ness only becomes undefined behavior when a write is attempted through the pointer/reference (possibly to help support legacy code that didn't follow proper const-correctness).

Advert answered 1/10, 2019 at 15:46 Comment(1)
had to read it twice, still not completely convinced, but I agree that it gives a good hint on how it is intended to work outKat

© 2022 - 2024 — McMap. All rights reserved.