Is This Undefined Behavior with Const_Cast

Is this undefined behavior with const_cast?

Quote from cppreference:

Even though const_cast may remove constness or volatility from any pointer or reference, using the resulting pointer or reference to write to an object that was declared const or to access an object that was declared volatile invokes undefined behavior.

So yes, modifying constant variables is undefined behavior. The output you see is caused by the fact that you tell the compiler that the value of a will never change, so it can just put a literal 0 instead of the variable a in the cout line.

Where is the undefined behavior when using const_cast ?

Line (2) has undefined behaviour. The compiler is at liberty to place constants in read-only memory (once upon a time in Windows this would have been a "data segment") so writing to it might cause your program to terminate. Or it might not.

Having to cast const-ness away when calling a poorly-defined library function (non-const parameter which should be const) is, alas, not unusual. Do it, but hold your nose.

Is const_cast(this) with a write operation undefined behaviour, if the actual object is non-const?

That's not undefined. That's exactly what const_cast is for. As long as the object itself is non-const then you can cast it away with const_cast and do the same things with it as a non-const pointer.

Do note that const_cast is usually considered a code smell and might indicate bad design.


As the standard says:

In the body of a non-static ([class.mfct]) member function, the
keyword this is a prvalue whose value is a pointer to the object for
which the function is called. The type of this in a member function of
a class X is X*. If the member function is declared const, the type of
this is const X*, if the member function is declared volatile, the
type of this is volatile X*, and if the member function is declared
const volatile, the type of this is const volatile X*.

The type of this is const X* in your case even though the object itself is non-const.


The standard says this about const_cast:

For two similar types T1 and T2, a prvalue of type T1 may be
explicitly converted to the type T2 using a const_­cast. The
result of a const_­cast refers to the original entity.

So, casting from const X* to X* is also legal.


Lastly, it says (albeit in a note):

[ 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-qualifier may produce undefined
behavior ([dcl.type.cv]). — end note  ]

And [dcl.type.cv] tells us:

Any attempt to modify ([expr.ass], [expr.post.incr], [expr.pre.incr])
a const object ([basic.type.qualifier]) during its lifetime
([basic.life]) results in undefined behavior.

Luckily, our this is pointing to a non-const object, so casting it and then modifying this object through the new non-const pointer doesn't trigger undefined behaviour.


Sorry Angew.

Undefined behaviour with const_cast

I was hoping that someone could clarify exactly what is meant by undefined behaviour in C++.

Technically, "Undefined Behaviour" means that the language defines no semantics for doing such a thing.

In practice, this usually means "don't do it; it can break when your compiler performs optimisations, or for other reasons".

What is puzzling me is why this appears to work and will modify the original const object but doesn't even prompt me with a warning to notify me that this behaviour is undefined.

In this specific example, attempting to modify any non-mutable object may "appear to work", or it may overwrite memory that doesn't belong to the program or that belongs to [part of] some other object, because the non-mutable object might have been optimised away at compile-time, or it may exist in some read-only data segment in memory.

The factors that may lead to these things happening are simply too complex to list. Consider the case of dereferencing an uninitialised pointer (also UB): the "object" you're then working with will have some arbitrary memory address that depends on whatever value happened to be in memory at the pointer's location; that "value" is potentially dependent on previous program invocations, previous work in the same program, storage of user-provided input etc. It's simply not feasible to try to rationalise the possible outcomes of invoking Undefined Behaviour so, again, we usually don't bother and instead just say "don't do it".

What is puzzling me is why this appears to work and will modify the original const object but doesn't even prompt me with a warning to notify me that this behaviour is undefined.

As a further complication, compilers are not required to diagnose (emit warnings/errors) for Undefined Behaviour, because code that invokes Undefined Behaviour is not the same as code that is ill-formed (i.e. explicitly illegal). In many cases, it's not tractible for the compiler to even detect UB, so this is an area where it is the programmer's responsibility to write the code properly.

The type system — including the existence and semantics of the const keyword — presents basic protection against writing code that will break; a C++ programmer should always remain aware that subverting this system — e.g. by hacking away constness — is done at your own risk, and is generally A Bad Idea.™

I can imagine a case where lack of awareness that C-style cast can result in a const_cast being made could occur without being noticed.

Absolutely. With warning levels set high enough, a sane compiler may choose to warn you about this, but it doesn't have to and it may not. In general, this is a good reason why C-style casts are frowned upon, but they are still supported for backwards compatibility with C. It's just one of those unfortunate things.

Does this const initialization through const_cast have undefined behaviour?

You modify an object defined as const. It doesn't matter when you do it, during initialization or not, it's still undefined behavior. Removing constness with const_cast is defined only if the const pointer was obtained from a non-const pointer to that object at some earlier stage. That's not your case.

The best thing you can do is

const bigLut_t& initializeConstBigLut()
{
static bigLut_t bigLot;

for(int i = 0; i < 100000; ++i) {
bigLut.at(i) = i;
}
return bigLut;
}

const bigLut_t constBigLut = initializeConstBigLut();

and hopefully the compiler will optimize out the static temporary.

Is const_cast on this pointer an undefined behavior?

const_cast has nothing to do with the behavior you experience. Your code may be simplified to the following:

  A() : x(0)
{
*this = A(10);
}

So, here the following happens if we want to create an object using a default constructor:

  1. before constructor body Memory for the object this is reserved.
  2. x(0) 0 is assigned to the member x of this.
  3. A(10) A new (unnamed) object of class A is created using the constructor A(int). This new object member x has value 10.
  4. this= Here the unnamed object is assigned (using the automatically generated copy assignment operator, which is field-wise) to *this. Thus, value of member x of this becames 10.
  5. after that line The temporary unnamed object is destroyed.
  6. this is returned.

This is perfectly legal and expected behavior.

const_cast const STL container, is it undefined behavior?

The C++ standard explicitly points out that casting away const for an object created via new const T, and modifying it, is Undefined Behavior.

E.g., the C++11 standard contains this example in its §7.1.6.1/4:

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

Examples in the standard are non-normative, just examples, and can be wrong. For example, in C++03 nearly all examples that used <iostream> were wrong (at that time it was formally necessary to also include <ostream>, but that was not the intention of the committee), and the expression examples in C++03 §5/4 were wrong, saying the behavior was unspecified rather than undefined (which might have reflected an original intention). However, the above example is correct.

This shows that dynamically allocated memory needs not be mutable: that modifying it, can have Undefined Behavior.

However, when e.g. a std::string is created, its allocation of a buffer (if any) occurs during execution of a constructor, at which point the object is not yet const, and the buffer is not allocated as const. So the buffer, if one is allocated, is not originally const. But regarding this particular example the std::string may use the small buffer optimization where it uses storage directly in the object, which would then be originally const (potentially allocated in read only memory). And this goes to the rational for the rule that no originally const object can be modified.


In addition to the read-only memory scenario, the rationale for UB includes that it can give compilers optimization possibilities.

As juanchopanza notes in a comment to the question,

Optimizers use this to do all kind of crazy stuff that assumes const objects won't be modified.

Breaking the assumptions of the optimizer by modifying an originally const object, can have disastrous and effectively unpredictable consequences.



Related Topics



Leave a reply



Submit