4

Is the code below bad practice or undefined behavior? Essentially i am calling a const func to modify a member which is not marked as mutable. Link to demo

Credits to Mehrdad for inspiring this question (his question Does this code subvert the C++ type system?) and david for minor demo improvements.

#include <iostream>
using namespace std;

struct BreakConst
{
    int v;
    int *p;
    BreakConst() { v = 0; p = &v; } 
    void break_stuff() const { ++*p; }
};
void f(const BreakConst& bc) {
    bc.break_stuff();
}

Original Version that most answers are based on:

Answered by David Rodríguez, jpalecek, Mehrdad
Yes: This is "Undefined behavior"

int main()
{
    const BreakConst bc;
    cout << bc.v << endl;   // 0
    bc.break_stuff();       // O:)
    cout << bc.v << endl;   // 1

    return 0;
}

New Alternative Question:

Answered by Mehrdad
No: This is not "Undefined behavior"

int main()
{
    BreakConst bc;
    cout << bc.v << endl;   // 0
    f(bc);                  // O:)
    cout << bc.v << endl;   // 1

    return 0;
}

Result:

0
1
Community
  • 1
  • 1
  • 4
    I'd argue that invoking undefined behavior *is* bad practice... :) – cHao Jun 18 '12 at 21:40
  • Might be useful to add something to the question to make it different from mine 15 hours ago... otherwise it's just a dupe with 3 lines less code. :\ – user541686 Jun 18 '12 at 21:41
  • @Mehrdad well you didnt exactly ask if its UB. The answer here don't exactly agree with eachother. -edit- i edited it to give you credit –  Jun 18 '12 at 21:43
  • @acidzombie24: Wasn't worried about the name, though thanks :) I was worried about getting split/duplicate discussions. If you're merely asking whether it's UB, then does my answer answer your question? Or is there something that it's missing? (Feel free to downvote it if it doesn't -- but mention why. I'm just trying to clarify this, that's all.) – user541686 Jun 18 '12 at 21:48
  • @Mehrdad I commented on your other comment to me (in your question) –  Jun 18 '12 at 21:54
  • @acidzombie24: I see, thanks. See my updated answer -- I quoted the section of the standard for you. :) – user541686 Jun 18 '12 at 22:15
  • 2
    @acidzombie24: wait, that's not fair, that edit _completely_ changed the correct answer! – Mooing Duck Jun 18 '12 at 22:22
  • Now it's just a dupe of [this question](http://stackoverflow.com/questions/357600/is-const-cast-safe)... – user541686 Jun 18 '12 at 22:34
  • Hope you don't mind the edit. But the answers were manly based on the original version and thus were confusing to read in context. – Martin York Jun 18 '12 at 22:48
  • @MooingDuck: Did it? I don't see whats different between the two changes except i tried to get rid of how the construct works debate. The function in question (break_stuff) is exactly the same... -edit- nm i see. Before the edit i had a const lvalue while after was a const reference. –  Jun 18 '12 at 22:58
  • Right, what makes the difference is where the the object is defined (in main). At first, it was `const` in `main`, and then you edited it to be non-const in `main`. – Mooing Duck Jun 18 '12 at 23:10
  • @MooingDuck: I was thinking the point where the code is illegal would be around the time the pointer would be modified (it may use cache values in registers not realizing it changed). I didn't think anything before that function had anything to do with what is defined or not (but i am wrong) –  Jun 18 '12 at 23:17
  • possible duplicate of [Does this code subvert the C++ type system?](http://stackoverflow.com/questions/11077566/does-this-code-subvert-the-c-type-system) – Luc Touraille Jun 19 '12 at 08:20

3 Answers3

3

In this case, I'd say it's undefined behaviour. bc is a const object, so are all its subobjects (less mutables)1, therefore bc.v should be, too and modifying a const object, however achieved, is UB2.

[1] C++03 3.9.3/3:

Each non-static, non-mutable, non-reference data member of a const-qualified class object is const- qualified...

[2] C++03 7.1.5.1/4:

Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.

EDIT responding to the edit of the question: No, the modified version of the code does not cause undefined behavior. It may be bad practice, but actually may be useful at times. You can eg. use it to implement iterators to your classes via const-iterators (DRY):

class const_iterator
{
public:
  const T& dereference() const; // complicated
};

class iterator : public const_iterator
{
public:
  T& dereference() const { return const_cast<T&>(const_iterator::dereference()); }
};

Of course that relies on the fact that iterators can only be made from mutable containers, that the const and non-const versions do not differ (no COW and such) etc., but that is fairly common.

jpalecek
  • 47,058
  • 7
  • 102
  • 144
  • The pointer wasn't to a const object when it was assigned, so to me that's the loophole that is being exploited. – Mark Ransom Jun 18 '12 at 21:37
  • 1
    @MarkRansom: The pointer *was* to a `const` object, taken during a time window where the object does not look `const`. But the compiler could move that object from the stack to a different page in memory and after construction mark the page as read-only. – David Rodríguez - dribeas Jun 18 '12 at 21:41
  • 1
    @DavidRodríguez-dribeas It can't move things so arbitrarily. To be sure, it's hard to imagine an implementation that would "misbehave." But yes, it's UB due to abuse of the constructor's `const` suspension privilege. – Potatoswatter Jun 18 '12 at 21:44
  • 1
    @Potatoswatter: What rule in the language would be violated if the object was moved? None AFAIK, which means that whether it is probable or improbable that the compiler would do this, the fact is that it is allowed. – David Rodríguez - dribeas Jun 18 '12 at 21:47
  • @dribeas Its address would change. C++ has no concept of moving an object, there is only move construction which initializes a *new* object. – Potatoswatter Jun 18 '12 at 21:54
  • I suspect it is UB. Since its changing in a function that sees it as cosnt. (Look at my code edit or david answer for example). –  Jun 18 '12 at 21:56
  • 1
    @DavidRodríguez-dribeas, I missed the fact that the object itself was declared `const` in `main` - it doesn't help that the question was modified to remove that qualification. I don't think the object can be moved because it would invalidate any pointers created in the constructor (as in this example), but the memory it resides in could theoretically be flipped to read-only after construction. I'm not aware of any hardware where that would be practical. – Mark Ransom Jun 18 '12 at 22:18
  • Nice edit. I am thinking if it was done through a function call it may sync up and be fine. However i am editing it through a ptr/alias so... i am not sure –  Jun 18 '12 at 22:20
  • Nice reference to the standard ;) – user541686 Jun 18 '12 at 22:25
  • @acidzombie24: No, "how the fuction sees it" is unimportant. See edit. – jpalecek Jun 18 '12 at 22:27
  • One slight question. Why is it different? In both cases the function calling `break_stuff` sees the object as `const`. What makes it defined in one version but not the other. -edit- oh i see. Its bc my original had a const lvalue while the alt is a const reference. –  Jun 18 '12 at 22:57
3

In your particular case it is undefined behavior as the object is const, not just the reference. It would be bad practice (and dangerously close to Undefined Behavior) in the following case:

void f( const BreakConst& b ) {
   bc.break_stuff();
}
int main() {
   BreakConst b;
   f( b );
}

The difference is that in this case the actual object is not const, even if the reference at the level of f is. The dangerously close to Undefined Behavior comes from the fact that the member function casting away const-ness cannot possibly know whether the object on which it has been called is const or not, so you have lost all control.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
2

After your edit:

No, it's not undefined. You're allowed to modify a mutable object through a const reference; it's completely allowed and legal.


Before your edit:

Yes, it must be undefined, because the standard (I'm looking at the draft here) clearly says:

§7.1.6.1.4

Except that any class member declared mutable (§77.1.1) can be modified, any attempt to modify a const object during its lifetime (§3.8) results in undefined behavior.

So yes -- since the member isn't mutable, modifying it is obviously undefined behavior.

I don't know why the rule is that way, whether this is intentional, whether it is indeed a loophole, whether it's also violating another rule, how you're supposed to tell just by looking at it, etc... but regarding the question of whether it's UB: yes, it's undefined according to the standard.

Community
  • 1
  • 1
user541686
  • 205,094
  • 128
  • 528
  • 886
  • Check out my edit (or david's answer). To answer your whats the difference question. My question is more specific. All the answers here are about UB vs not UB. While your question had a lot talking about the rules of const. Thats why i felt that a different phrasing would help. I hope it didnt bother you too much. Also +1 –  Jun 18 '12 at 22:02
  • 1
    The question changed, and now it is no longer undefined. – Mooing Duck Jun 18 '12 at 22:25