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 const
ness 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 const
ness of an object that is not really const
and modify it fine. However, in the second call, in setOscillator
you are casting away the const
ness 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
Why Does the Use of 'New' Cause Memory Leaks
How to Remove Unused C/C++ Symbols With Gcc and Ld
How to Have Functions Inside Functions in C++
C++ Lambda With Captures as a Function Pointer
How to Handle Wrong Data Type Input
What Is the Lifetime of the Result of Std::String::C_Str()
Initializer Lists and Rhs of Operators
How to Use the Pi Constant in C++
Should I Prefer Pointers or References in Member Data
C++ Static Polymorphism (Crtp) and Using Typedefs from Derived Classes
Is Std::Unique_Ptr≪T≫ Required to Know the Full Definition of T
How to Find the Location of the Executable in C
Is the Pimpl Idiom Really Used in Practice
Sfinae Working in Return Type But Not as Template Parameter
Why Does C++ Require a Cast For Malloc() But C Doesn'T
Why Does Rand() Yield the Same Sequence of Numbers on Every Run