20

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?

Kevin Panko
  • 8,356
  • 19
  • 50
  • 61
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • Hmm, after posting my answer I realized it could as well apply to your stack-allocated object. Can you provide some more information on why the stack example is UB? – Andreas Brinck Dec 18 '09 at 10:56
  • The stack example is just the most obvious. For example, you call some function and pass a const pointer to such an object and somewhere very deep down the call stack a const_cast is done and a non-const method is called - welcome the UB, very bad for portability. – sharptooth Dec 18 '09 at 11:00
  • @sharptooth Is this paragraph 3.10/15 in action? – Andreas Brinck Dec 18 '09 at 11:05
  • No, that's 7.1.5.1/4 - modifying a const object. – sharptooth Dec 18 '09 at 11:15
  • 1
    Related question: http://stackoverflow.com/questions/1542200/is-using-constcast-for-read-only-access-to-a-const-object-allowed – CB Bailey Dec 18 '09 at 11:20

6 Answers6

20

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;
}
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 1
    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 Dec 18 '09 at 11:09
  • Because undefined behaviour may be defined on your target platform. const_cast sacrifices portability - which frequently is an OK tradeoff. – Pontus Gagge Dec 18 '09 at 11:12
  • @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 Dec 18 '09 at 11:14
  • 2
    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. – CB Bailey Dec 18 '09 at 11:16
  • @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 Dec 18 '09 at 11:17
  • 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`. – CB Bailey Dec 18 '09 at 11:18
  • 2
    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. Dec 18 '09 at 11:27
  • @Everyone Ok, I get it, does someone have a reference to the relevant paragraph in the standard? – Andreas Brinck Dec 18 '09 at 11:28
  • 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 Dec 18 '09 at 11:31
  • 1
    @Stefan I just read sharptooths reference to the standard and your example is not UB. – Andreas Brinck Dec 18 '09 at 11:42
  • @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 Dec 18 '09 at 11:51
  • 1
    Yes, because the object itself wouldn't be const. Writes through `q` would then be fine. – CB Bailey Dec 18 '09 at 11:53
  • 5
    Neat! I had no idea `const` could be used with `new` like that. – Michael Myers Dec 18 '09 at 23:17
12

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
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 5
    You can _can_ delete through a const pointer and a pointer to const. – CB Bailey Dec 18 '09 at 11:02
  • 1
    `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. – CB Bailey Dec 18 '09 at 11:14
  • Thanks for that, I've clarified my answer. – Greg Hewgill Dec 18 '09 at 11:47
2

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
  • 161,384
  • 21
  • 275
  • 467
1

Obviously:

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

Foo* foo = new Foo;
const_cast<int&>(foo->Bar); // don't do this.
MSalters
  • 173,980
  • 10
  • 155
  • 350
1

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!
WolfgangP
  • 3,195
  • 27
  • 37
0

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
  • 2,386
  • 1
  • 14
  • 21
  • 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 Dec 18 '09 at 12:42