views:

389

answers:

6

In C++ a stack-allocated object can be declared const:

const Class object;

after that trying to call a non-const method on such object is undefined behaviour:

const_cast<Class*>( &object )->NonConstMethod(); //UB

Can a heap-allocated object be const with the same consequences? I mean is it possible that the following:

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

is also undefined behaviour?

+9  A: 

In your heap example, new returns a pointer to non-const. The fact that you've stored it in a pointer to const (and then const_casted it back to a pointer to non-const) doesn't change the fact that the object itself is not const in the same way as the stack-allocated one is.

However, you can create a const object on the heap:

const Class* object = new const Class();

In such a case, casting to a pointer to non-const and calling a non-const method would be the same situation as the const stack-allocated object.

(The idea of creating a const object on the heap was new to me, I had never seen that before. Thanks to Charles Bailey.)

Greg Hewgill
You can _can_ delete through a const pointer and a pointer to const.
Charles Bailey
`new` returns a pointer that is an rvalue. It can be assigned to a const or non-const pointer. The result of new can be a pointer to const or a pointer to non-const object depending on the type of object 'newed'. In the case of a non-const object, the pointer can be assigned to a pointer to non-const or to a pointer to const (usual) rules, but if you 'new' a const object you can only assign to a pointer to const.
Charles Bailey
Thanks for that, I've clarified my answer.
Greg Hewgill
+11  A: 

Yes. It's legal to construct and destroy a const heap object. As with other const objects, the results of manipulating it as a non-const object (e.g. through a const_cast of a pointer or reference) causes undefined behaviour.

struct C
{
        C();
        ~C();
};

int main()
{
        const C* const p = new const C;

        C* const q = const_cast<C*>(p); // OK, but writes through q cause UB

        // ...

        delete p; // valid, it doesn't matter that p and *p are const

        return 0;
}
Charles Bailey
Ok, feeling a bit stupid here but what is the purpose of const_cast if invoking methods on the resulting pointer can always be UB?
Andreas Brinck
Because undefined behaviour may be defined on your target platform. const_cast sacrifices portability - which frequently is an OK tradeoff.
Pontus Gagge
@Pontus I still don't get it; does using `const_cast` always cause UB? (according to the standard, I don't care if the result on some specific platform happen to be defined).
Andreas Brinck
If you're in a const method or have been given a pointer or reference to const and you *know* that the object itself is actually not const then it is legal to cast away constness. It's usually an inteface work-around.
Charles Bailey
@Andreas Brinck. UB is only trying to modify an object that is declared const. You can cast away const if the object was initialyy declared as non-const.
sharptooth
A `const_cast` never causes UB. Using the result of a `const_cast` *can* cause UB if the operation is a 'write' and the underlying object actually is `const`.
Charles Bailey
AFAIK the intended use case of const_cast is to cast away const to pass things to legacy code that doesn't use const even though it doesn't modify anything
jk
@Everyone Ok, I get it, does someone have a reference to the relevant paragraph in the standard?
Andreas Brinck
Andreas, besides what jk said, const_cast's often used to implement stuff like const operator[] via nonconst operator[]. Not sure if it's an UB usage, but everyone does it.
Stefan Monov
@Stefan I just read sharptooths reference to the standard and your example is not UB.
Andreas Brinck
@Charles, Would it have made any difference if `p` had been allocated with just `new C` instead of `new const C`? (I'm guessing yes).
Andreas Brinck
Yes, because the object itself wouldn't be const. Writes through `q` would then be fine.
Charles Bailey
+1 Thanks for the information!
Andreas Brinck
Neat! I had no idea `const` could be used with `new` like that.
Michael Myers
+1  A: 

Obviously:

struct Foo {
  const int Bar;
  Foo() : Bar(42) { }
};

Foo* foo = new Foo;
const_cast<int*>(foo->Bar); // don't do this.
MSalters
+1  A: 

Yes, a heap-allocated object can be const. Consider this excerpt from the example in 7.1.5.1/5:

const int* ciq = new const int (3);    // initialized as required
int* iq = const_cast<int*>(ciq);       // cast required
*iq = 4;                               // undefined: modifies a const object

The example you gave in the question is fine because you're not asking new to make a const object; you're just storing the result in a pointer-to-const.

Rob Kennedy
+1  A: 

const_cast can cause UB when the object is actually read-only (for example, the compiler can create such objects when you use hard coded strings in your code, by placing them in certain memory areas that are read only) for some reason. This will not happen with heap allocated objects, no matter how you keep their reference (const pointer, const reference, whatever).

rmn
Read-only memory is not the issue. The issue is whether an object is const. Undefined behavior *will* happen with heap-allocated objects if they are allocated const and you write to them via some non-const access path (such as one you get from using const_cast). Your particular implementation might take the liberty of defining what happens in that situation, but as far as the *standard* is concerned, it's still undefined.
Rob Kennedy
+1  A: 

Don't forget mutable members

It won't be undefinied behaviour if the NonConstMethod only modifies mutable qualified members (see 7.1.5.1 (4)) of a const qualified class. Yes, otherwise it's undefined behaviour.

const A* p = new(const A);
A *q = const_cast<A*>(p);
q->NonConstMethodThatModifiesMembers();             // undefined behaviour!
q->NonConstMethodThatOnlyModifiesMutableMembers();  // defined behaviour!
Wolfgang Plaschg
Could you please explain how you reach that conclusion from the portion of the standard you quoted?
Rob Kennedy
OK, you're right. I assumed that an rvalue is always non-modifyable but that assumption doesn't hold.
Wolfgang Plaschg
You seem to have turned your answer into a question.
Rob Kennedy
I modified my answer, because the first one was incorrect. I hope my new answer adds another aspect to the discussion.
Wolfgang Plaschg