Is Const_Cast Safe

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

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.

C++ const cast, unsure if this is secure

As an example of evil behavior: the interaction with gcc's Copy On Write implementation.

#include <string>
#include <iostream>

int main() {
std::string const original = "Hello, World!";
std::string copy = original;

char* c = const_cast<char*>(copy.c_str());
c[0] = 'J';

std::cout << original << "\n";
}

In action at ideone.

Jello, World!

The issue ? As the name implies, gcc's implementation of std::string uses a ref-counted shared buffer under the cover. When a string is modified, the implementation will neatly check if the buffer is shared at the moment, and if it is, copy it before modifying it, ensuring that other strings sharing this buffer are not affected by the new write (thus the name, copy on write).

Now, with your evil program, you access the shared buffer via a const-method (promising not to modify anything), but you do modify it!

Note that with MSVC's implementation, which does not use Copy On Write, the behavior would be different ("Hello, World!" would be correctly printed).

This is exactly the essence of Undefined Behavior.

C++: Why is const_cast evil?

Because you're thwarting the purpose of const, which is to keep you from modifying the argument. So if you cast away the constness of something, it's pointless and bloating your code, and it lets you break promises that you made to the user of the function that you won't modify the argument.

In addition, using const_cast can cause undefined behaviour. Consider this code:

SysOscillatorBase<int> src;
const SysOscillatorBase<int> src2;

...

aFieldTransformer.setOscillator(src);
aFieldTransformer.setOscillator(src2);

In the first call, all is well. You can cast away the constness of an object that is not really const and modify it fine. However, in the second call, in setOscillator you are casting away the constness of a truly const object. If you ever happen to modify that object in there anywhere, you are causing undefined behaviour by modifying an object that really is const. Since you can't tell whether an object marked const is really const where it was declared, you should just never use const_cast unless you are sure you'll never ever mutate the object ever. And if you won't, what's the point?

In your example code, you're storing a non-const pointer to an object that might be const, which indicates you intend to mutate the object (else why not just store a pointer to const?). That might cause undefined behaviour.

Also, doing it that way lets people pass a temporary to your function:

blah.setOscillator(SysOscillatorBase<int>()); // compiles

And then you're storing a pointer to a temporary which will be invalid when the function returns1. You don't have this problem if you take a non-const reference.

On the other hand, if I don't use const_cast, the code won't compile.

Then change your code, don't add a cast to make it work. The compiler is not compiling it for a reason. Now that you know the reasons, you can make your vector hold pointers to const instead of casting a square hole into a round one to fit your peg.

So, all around, it would be better to just have your method accept a non-const reference instead, and using const_cast is almost never a good idea.


1 Actually when the expression in which the function was called ends.

Is const_cast safer than normal cast?

It's safer in the sense that you won't get a cast that's something other than just removing const:

int main()
{
const char i=5;
int *ptr;
ptr=(int*)&i; // the compiler won't complain

ptr=const_cast<int*>(&i); // will fail, since `i` isn't an int
return 0;
}

which doesn't necessary mean that the const_cast<> is safe:

const int i=5;

int main()
{
int const& cri(i);

int& ri = const_cast<int&>(cri); // unsafe

ri = 0; // will likely crash;

return 0;
}

Is const_cast(iterator-second) safe?

Yes, it is safe. Even if a const_iterator is different from an iterator, the objects in the map are the same. As long as all the map contents were created as mutable std::deque<Session*> objects, it's OK to cast the constness away.

Of course, it's possible you're violating some invariant the constness was meant to convey, but that's possible with any const_cast taken in isolation.

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

Is it allowed to cast away const on a const-defined object as long as it is not actually modified?

Yes. This is entirely legal. (It is dangerous, but it is legal.) If you (attempt to) modify a an object declared const, then the behaviour is undefined.

From n4659 (which is the last draft of C++17), section 10.1.7.1 [dcl.type.cv] para 4:

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

My emphasis. That is from C++17, but this has been true of all versions of C++.

If you look at the section on const_cast there is a note that

[ 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-qualifier76 may produce undefined
behavior (10.1.7.1). — end note ]

Notes are not normative, but this strongly implies that obtaining a non-const reference or pointer to a const object is legal. It is the write that is not allowed.

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).



Related Topics



Leave a reply



Submit