behavior of const_cast in C++
As-written the way you're doing this is undefined behavior. If you wanted to see the effects of const_cast<>
in a defined manner:
int a = 5; // note: not const. regular object.
const int& cref = a; // const-reference to same object.
cref = 7; // illegal. cref is a const reference.
const_cast<int&>(cref) = 7; // legal. the original object a is not const.
The only reason this is defined behavior is due to the non-const nature of the original variable, a
. You cannot take an outright-const object and simply cast away the const-ness, which is what your posted code did. (at least as it has been explained to me on several occasions).
behaviour of const_cast
it helps to cast away constness of an expression of type Type
No, Type
is the type of the result, not the type of the operand.
What i think is const of this pointer should be casted away
this
has type const ConstTest*
. const_cast<ConstTest*>(this)
has type ConstTest*
. That's what "casting away const" from a pointer-to-const means.
I feel code should have been
ConstTest *c =
const_cast<ConstTest>(*this)
The result of const_cast<T>
has type T, that's how it's defined. Maybe you would have defined it differently, but tough luck, you don't get a ConstTest*
by writing const_cast<ConstTest>
, you get it by writing const_cast<ConstTest*>
. Your preferred syntax is not available.
You can either do ConstTest &c = const_cast<ConstTest&>(*this)
or ConstTest *c = const_cast<ConstTest*>(this)
, so pick your favorite.
The result of a const_cast expression is an rvalue unless Type is a
reference type. In this case, the result is an lvalue.why so and why it is not true in case of pointers?
It is true of pointers. ConstTest*
is not a reference type, and the result of const_cast<ConstTest*>(this)
is an rvalue. You then assign that value to the variable c
.
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.
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.
What are Legitimate uses of const_cast
You are right, uses of const_cast
often indicates a design flaw, or an API that is out of your control.
However, there is an exception, it's useful in the context of overloaded functions. I'm quoting an example from the book C++ Primer:
// return a reference to the shorter of two strings
const string &shorterString(const string &s1, const string &s2)
{
return s1.size() <= s2.size() ? s1 : s2;
}
This function takes and returns references to const string
. We can call the function on a pair of non-const string
arguments, but we’ll get a reference to a const string
as the result. We might want to have a version of shorterString
that, when given non-const arguments, would yield a plain reference. We can write this version of our function using a const_cast
:
string &shorterString(string &s1, string &s2)
{
auto &r = shorterString(const_cast<const string&>(s1),
const_cast<const string&>(s2));
return const_cast<string&>(r);
}
This version calls the const version of shorterString
by casting its arguments to references to const
. That function returns a reference to a const string
, which we
know is bound to one of our original, non-const arguments. Therefore, we know it is safe to cast that string back to a plain string&
in the return.
How to use const_cast?
You are not allowed to const_cast
variables that are actually const
. This results in undefined behavior. const_cast
is used to remove the const-ness from references and pointers that ultimately refer to something that is not const
.
So, this is allowed:
int i = 0;
const int& ref = i;
const int* ptr = &i;
const_cast<int&>(ref) = 3;
*const_cast<int*>(ptr) = 3;
It's allowed because i
, the object being assigned to, is not const
. The below is not allowed:
const int i = 0;
const int& ref = i;
const int* ptr = &i;
const_cast<int&>(ref) = 3;
*const_cast<int*>(ptr) = 3;
because here i
is const
and you are modifying it by assigning it a new value. The code will compile, but its behavior is undefined (which can mean anything from "it works just fine" to "the program will crash".)
You should initialize constant data members in the constructor's initializers instead of assigning them in the body of constructors:
Student(const Student & s)
: Person(p.getName(), p.getEmailAddress(), p.getBirthDate()),
school(0),
studentNumber(s.studentNumber)
{
// ...
}
const_cast and UB
a) Firstly, the use of 'may'. Why is
it 'may'? Other places in the Standard
are very definitive about the
undefined behavior
Don't look too deeply into the use of the word may here. The point is, casting away constness in this case causes undefined behavior.
The C++ standard uses "may" or "might" often, as in:
1.3.12: Undefined behavior may also be expected when this International
Standard omits the description of any
explicit definition of behavior.
Emphasis mine. Basically, the standard uses the word "may" as in "is allowed to".
b) Why is that the casting away the
constness of a originally const object
not straight away 'undefined
behavior'. Why is it that a write is
required for UB to be triggered?
A write triggers UB because it's possible that const objects can be stored in read-only memory on certain platforms.
Is const_cast safe?
const_cast
is safe only if you're casting a variable that was originally non-const
. For example, if you have a function that takes a parameter of a const char *
, and you pass in a modifiable char *
, it's safe to const_cast
that parameter back to a char *
and modify it. However, if the original variable was in fact const
, then using const_cast
will result in undefined behavior.
void func(const char *param, size_t sz, bool modify)
{
if(modify)
strncpy(const_cast<char *>(param), sz, "new string");
printf("param: %s\n", param);
}
...
char buffer[16];
const char *unmodifiable = "string constant";
func(buffer, sizeof(buffer), true); // OK
func(unmodifiable, strlen(unmodifiable), false); // OK
func(unmodifiable, strlen(unmodifiable), true); // UNDEFINED BEHAVIOR
Related Topics
Opengl: Glflush() VS. Glfinish()
Class Members and Explicit Stack/Heap Allocation
How to Get Size C++ Dynamic Array
Creating JSON Arrays in Boost Using Property Trees
Need a Fast Random Generator for C++
How Does the Main() Method Work in C
Cmake: Project Structure with Unit Tests
Abstract Class VS Interface in C++
Using Libstdc++ Compiled Libraries with Clang++ -Stdlib=Libc++
Optimizing Member Variable Order in C++
Differencebetween Include_Directories and Target_Include_Directories in Cmake
Is It More Efficient to Copy a Vector by Reserving and Copying, or by Creating and Swapping