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
keywordthis
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 classX
isX*
. If the member function is declared const, the type of
this is constX*
, if the member function is declaredvolatile
, the
type of this isvolatile X*
, and if the member function is declared
const volatile
, the type of this isconst 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
andT2
, a prvalue of typeT1
may be
explicitly converted to the typeT2
using aconst_cast
. The
result of aconst_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 aconst
-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 const
ness — 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:
- before constructor body Memory for the object
this
is reserved. - x(0)
0
is assigned to the memberx
ofthis
. - A(10) A new (unnamed) object of class
A
is created using the constructorA(int)
. This new object memberx
has value10
. - this= Here the unnamed object is assigned (using the automatically generated copy assignment operator, which is field-wise) to
*this
. Thus, value of memberx
ofthis
becames10
. - after that line The temporary unnamed object is destroyed.
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
Reinterpret_Cast VS. C-Style Cast
Compute Median of Values Stored in Vector - C++
Uninitialized Pointers in Code
Why Don't the Std::Fstream Classes Take a Std::String
Getline() Does Not Work If Used After Some Inputs
Changing Function Access Mode in Derived Class
Inheritance or Composition: Rely on "Is-A" and "Has-A"
(Partially) Specializing a Non-Type Template Parameter of Dependent Type
How to Avoid Memory Leak with Shared_Ptr
How to Read Directly from Physical Memory on Windows
Understanding Return Value Optimization and Returning Temporaries - C++
All Combinations of K Elements Out of N
C/C++ Changing the Value of a Const
How to Completely Disable Calls to Assert()
Error: Variable "Cannot Be Implicitly Captured Because No Default Capture Mode Has Been Specified"
How to Iterate Over a Priority_Queue
What Legitimate Reasons Exist to Overload the Unary Operator&