11

In C++ it is possible to allocate a const object on heap:

const Class* object = new const Class();
const_cast<Class*>( object )->NonConstMethod(); // UB

so that attempt to write into an object will be UB.

I don't get how such an object will be different from a heap-allocated object that is not declared const:

const Class* object = new Class();

I mean when I allocate an object on stack it goes to automatic storage which is implementation-specific and so there might be some implementation-specific means that would allow allocating const objects in some special way that would yield UB when I write to an object.

Yet whenever I use new the compiler is required to emit operator new() function invokation and that function can't possibly do anything different - it just allocates memory in a uniform manner regardless of whether there was const in my code.

How is a const heap-allocated object different from a non-const one and how is undefined behavior possible if I try to modify it?

Community
  • 1
  • 1
sharptooth
  • 167,383
  • 100
  • 513
  • 979

5 Answers5

9

It's different because the created object has a different type (const Class instead of just Class), and it is undefined behavior because the standard says so.

That's the short version. There doesn't have to be a reason. (if anything, the inverse is true. There doesn't have to be a reason for something being UB. UB is the default state. It's only when there is a reason that something becomes well-defined)

As for what it means in practice, or whether it can actually cause problems if you treat the object as non-const, the hardware is unlikely to do anything different. The const object obviously won't be written into some kind of read-only memory (because that's not possible), and the memory page it's in probably isn't going to be flagged as read-only once the object has been allocated.

But the compiler is allowed to assume that the object is const. So it may optimize or transform the code in a way that is legal if the object is guaranteed to be unchanging, but which breaks if the object is modified halfway through.

It's really not about how the object is stored in the hardware. Const or no const rarely makes a difference on the hardware level. But it makes a difference in the type system, and it makes a difference in how the compiler is able to transform the code.

If you tell the compiler that an object is const, then the compiler believes you, and generates code on the assumption that the object is const.

jalf
  • 243,077
  • 51
  • 345
  • 550
  • +1, but I think this goes far beyond "assume". A const _pointer_ means you promise not to modify it (via that pointer) so it's probably constant, viewed through that pointer. A const _object_, on the other hand, is just that, constant. You said so, explicitly. Which means that it _cannot_ ever change,(unless the programmer is being deliberately stupid). Which means that the compiler may not just asssume, but must know and must consider that e.g. calling the same member several times will always be the exact identical result with identical side effects (otherwise the object _is not_ const). – Damon Dec 02 '11 at 12:25
  • ... wich brings up another interesting issue. You could create a const object from a type that is very much not const (e.g. something that modifies global state from inside a member function). It is still legal to create a const object of that type, though :( – Damon Dec 02 '11 at 12:26
  • The compiler doesn't *have* to consider that calling the same member function will return the same results (and that's also not guaranteed to be true for a const object). – jalf Dec 02 '11 at 12:34
  • 1
    @Damon: jalf is correct; a method which always returns the same results when called with the same arguments is called a "pure" method; there is no requirement whatsoever that every method on a const object be pure! Imagine for example a const object with a method that returns the current time. Nothing in the object is mutated, but the method can still return arbitrarily different results. – Eric Lippert Dec 02 '11 at 18:47
  • 1
    @Damon "_A const pointer means you promise not to modify it_" no it doesn't. – curiousguy Dec 02 '11 at 22:35
  • @Damon "_very much not const (e.g. something that modifies global state from inside a member function)._" how is that "much not const"? – curiousguy Dec 02 '11 at 22:35
  • @Eric Lippert: Your example of a const object having a method that returns time is syntactically correct, but technically incorrect. Like I said in my above comment, it is funnily possible to create an object which is const and isn't (because its members aren't). Also do note that a _pure_ function is allowed to access global state, insofar a function returning time is indeed pure, it just isn't const -- that would not allow to access some global state such as a timer. Now, how would an object that has non-const behaviour qualify as const? Yet it's possible to do such a thing. – Damon Dec 04 '11 at 11:42
  • @Damon: in a const object, all members variables are implicitly const, and non-const member functions can't be called. And it makes no sense to say that an object "is const and isn't". `const` is a keyword with a precise meaning in C++. It does not simply mean "constant" (I can create an object which never changes, and therefore is constant, even if it does not use the `const` keyword even once. On the other hand, I can create a `const` object which can still be modified). Beyond that, I'm not really sure what you're trying to say. – jalf Dec 04 '11 at 12:41
  • but the definition of a pure function is typically that it does *not* access any mutable state whatsoever. A function is pure if its output is determined solely from its inputs. So a pure function certainly can't access a global timer. – jalf Dec 04 '11 at 12:42
  • @Damon: That we differ slightly on the details of what makes a method "pure" is not particularly relevant. (A method that returns the time is not pure.) My point is that I agree with jalf: "const" has a particular meaning in C++ and it is important to reason about it correctly. Now, if your point is that "const" does not actually enforce the behaviour in C++ that you would like to have enforced, *I heartily agree with you*. I consider "const" to be badly broken in C++; it does not give me any guarantee that I can actually use to advantage. – Eric Lippert Dec 04 '11 at 15:17
4

There is no difference in the object. There is a difference in the (compile-time) type of the variable(s) used to refer to the memory area.

This is semantic friction only: the variable is different, the actual memory used by the data bits is const/volatile agnostic.

For a very amusing and enlightening story describing similar semantic friction see this all-time-favourite answer by Eric Lippert:

On the Undefined Behaviour

Treating const data in a non-const way can lead to Undefined Behaviour, because the compiler is allowed to do certain optimizations based on the knowledge that a const variable won't change1. Changing it none-the-less (e.g. by const_cast<>) can lead to erronous results because the compiler's assumptions are actively negated.

1 Note that volatile is there to help in cases where const variables can get modified concurrently. You can see how const is a 'local' won't/can't touch promis, whereas volatile says: 'don't assume this won't change, even though it is not being written to in this code segment'. `

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
1

With current compilers, there is no technical difference. Undefined Behaviour includes things miraculously working.

I dimly remember that there was a proposal to have const-qualified constructors that would allow special-casing instances where the object would be const immediately after construction; that would be useful e.g. for string classes that would allocate less memory if they didn't need to expect the string to grow.

Simon Richter
  • 28,572
  • 1
  • 42
  • 64
1

It's implementation-dependent, but it probably isn't any different. It could be modified. But the compiler will reject code that tries to.

const is more about refusing to compile code that modifies the object than about actually making it impossible to modify by any means. It's a note to the compiler that says "don't let me try to change this by mistake."

Wyzard
  • 33,849
  • 3
  • 67
  • 87
-1

There is no difference whatsoever between const and non-const object. There is no undefined behaviour neither in your exemple. What UB do you expect ? By calling the non-const function, you'll get the behaviour you expect from it.

Let me remind you that some fields may be declared mutable so that the object is not const as a whole. Not mentioning that you can even abuse in syuch a way that the compiler will not be aware of concrete non-constness of your member function:

class A {
public:
    A() : value_(0) {}
    void abuse() const { const_cast<A*>(this)->value_  = 1 ; }
    int value_;
    };

void test() {
    const A a;
    std::cout << a.value_ << std::endl;
    a.abuse() ;
    std::cout << a.value_ << std::endl;
    }

There we maybe get UB.

reder
  • 1,098
  • 7
  • 9