Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: Why is const_cast evil?

I keep hearing this statement, while I can't really find the reason why const_cast is evil.

In the following example:

template <typename T>
void OscillatorToFieldTransformer<T>::setOscillator(const SysOscillatorBase<T> &src)
{
    oscillatorSrc = const_cast<SysOscillatorBase<T>*>(&src);
}

I'm using a reference, and by using const, I'm protecting my reference from being changed. On the other hand, if I don't use const_cast, the code won't compile. Why would const_cast be bad here?

The same applies to the following example:

template <typename T>
void SysSystemBase<T>::addOscillator(const SysOscillatorBase<T> &src)
{
    bool alreadyThere = 0;
    for(unsigned long i = 0; i < oscillators.size(); i++)
    {
        if(&src == oscillators[i])
        {
            alreadyThere = 1;
            break;
        }
    }
    if(!alreadyThere)
    {
        oscillators.push_back(const_cast<SysOscillatorBase<T>*>(&src));
    }
}

Please provide me some examples, in which I can see how it's a bad idea/unprofessional to use a const_cast.

Thank you for any efforts :)

like image 338
The Quantum Physicist Avatar asked May 08 '12 12:05

The Quantum Physicist


People also ask

What is the point of const_cast?

const_cast is one of the type casting operators. It is used to change the constant value of any object or we can say it is used to remove the constant nature of any object. const_cast can be used in programs that have any object with some constant value which need to be changed occasionally at some point.

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.

Should I use const_cast?

If you are careful, you can use const_cast to allow you to retain the const-correctness of your code while still using the library. But you should try to bottleneck those situations to make them as few as possible. tl;dr: const_cast is likely something you should never use.

Is const cast undefined behavior?

const_cast is perhaps the most rarely used cast operator because it is undefined behavior to remove const or volatile from a variable if the variable was declared const or volatile in the first place.


2 Answers

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.

like image 193
Seth Carnegie Avatar answered Sep 20 '22 17:09

Seth Carnegie


by using const, I'm protecting my reference from being changed

References can't be changed, once initialized they always refer to the same object. A reference being const means the object it refers to cannot be changed. But const_cast undoes that assertion and allows the object to be changed after all.

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

This isn't a justification for anything. C++ refuses to compile code that may allow a const object to be changed because that is the meaning of const. Such a program would be incorrect. const_cast is a means of compiling incorrect programs — that is the problem.

For example, in your program, it looks like you have an object

std::vector< SysOscillatorBase<T> * > oscillators

Consider this:

Oscillator o; // Create this object and obtain ownership
addOscillator( o ); // cannot modify o because argument is const
// ... other code ...
oscillators.back()->setFrequency( 3000 ); // woops, changed someone else's osc.

Passing an object by const reference means not only that the called function can't change it, but that the function can't pass it to someone else who can change it. const_cast violates that.

The strength of C++ is that it provides tools to guarantee things about ownership and value semantics. When you disable those tools to make the program compile, it enables bugs. No good programmer finds that acceptable.

As a solution to this particular problem, it looks likely that the vector (or whatever container you're using) should store the objects by value, not pointer. Then addOscillator can accept a const reference and yet the stored objects are modifiable. Furthermore, the container then owns the objects and ensures they are safely deleted, with no work on your part.

like image 28
Potatoswatter Avatar answered Sep 21 '22 17:09

Potatoswatter