How to Use Const_Cast

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)
{
// ...
}

Correct usage(s) of const_cast

const_cast is also used to remove volatile modifiers, as put into practice in this (controversed) article:

http://www.drdobbs.com/184403766

How does const_cast works?

Not the way it works exactly, but you can think of constcorrectness being checked in an early stage of compilation and after that const doesnt matter anymore. Ie all that happens is that you tell the compiler to treat the object as non-const.

Consider this example:

int main() {
int x = 5;
const int& y = x;
const_cast<int&>(y) = 7; // ok, because x is actually not const
}

Once the compiler made sure that the code is const correct, it is equivalent to

int main() {
int x = 5;
int& y = x;
y = 7;
}

Just don't forget that you are not allowed to cast const away on something that actually is const. In the example if x was const then you could still cast away constness on y, get no compiler error, but undefined behaviour at runtime.

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)
{
// ...
}

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.

casting away the constness using const_cast

const_cast is applied to expressions, not objects, and itself is an expression as well:

§ 5.2.11 [expr.const.cast]/p1:

The result of the expression const_cast<T>(v) is of type T. If T is an lvalue reference to object type, the
result is an lvalue; if T is an rvalue reference to object type, the result is an xvalue; otherwise, the result
is a prvalue and the lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard
conversions are performed on the expression v

  1. const_cast<int*>(i)=m;

This call is invalid, because the left side of the assignment has a prvalue value-category, and an int* prvalue doesn't support an assignment. The correct syntax would be const_cast<int*&>(i)=m;, but since i in your example was declared const, it would invoke undefined behavior .


  1. const_cast<int*>(*i1)=l;

Dereferencing a pointer of type int* creates an expression of an lvalue value-category, and because the cast expression is on the left side of the assignment, it should be a cast to an lvalue reference type, namely const_cast<int&>(*i1)=10; (provided that whatever i1 points to was not declared const).


  1. const_cast<int>(a)=b;

The const_cast<int>(a) part itself is valid, in particular you can apply a const_cast to an expression representing an object which is not of a pointer type neither of a reference type. But since it's on the left side of the assigment it won't compile. And even if you change it to const_cast<int&>(a)=b; it will trigger undefined behavior, because a is declared const .


§ 7.1.6.1 [dcl.type.cv]/p4:

Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const
object during its lifetime (3.8) results in undefined behavior.

change the value of const_cast ptr/ref doesn't change the original object value?

If you spell out the type of ptr2i you get:

const int * ptr2i = &i;  // since i is const

Now you can const_cast this const int * to an int *:

auto pp = const_cast<int*>(ptr2i);  // ok

But the pointed at variable i has type const int, so if you then try to modify this pointed at value:

*pp = 456;  // oops, UB since you are attempting to modify i

you invoke undefined behavior. This can result in a program that does anything, including showing different values at the same address.

The same restrictions apply when you cast a const int & to an int & and then try to modify it.

Should I use const_cast?

const_cast is safe only, if pointer is really pointing to something which is non-const, so modifying it is not a problem. If the value is originally const, then using const_cast to get rid of the constness and then writing to it is basically undefined behaviour (for example, other parts of the code may assume value does not change, so your modification only takes effect sometimes).

So definitely use this version:

const char* charPointerr = params[0];

In general, if you need to resort to const_cast, you are probably doing something... if not wrong, then at least ugly. And if you are not sure if it is just ugly or outright wrong, then it's probably wrong.


Example of something where things can go horribly wrong due to undefined behaviour like this:

const bool do_it = true;
bool *ptr = const_cast<bool*>(&do_it);
*ptr = false;

// optimizer probably thinks this is always true
if (do_it) initiate_nuclear_first_strike();

While developing using debug build, everything seems to work. Then, release build is deployed (without testing, of course, because in hurry), and BOOM, end of the world. UB is dangerous like that (a side note: effects of UB can also be totally obscure and not as easily understandable as this example).

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



Leave a reply



Submit