Intro
This is the second article of my small mini series about maybe not so well known C++ features / pattern / programming techniques for which I found a practical usage during programming on my private projects.
This time the topic is about const_cast<T&>
.
const_cast is considered evil
It is more or less indisputable, that – like goto
– a const_cast<>
is considered as evil and bad practice / design in almost all of the cases.
I created a short example on godbolt that illustrates the evil usage of a const_cast
. Here is the link: Godbolt evil example.
There a const member of a const object will be changed from outside without any good reason / need.
The reason why this is considered bad practice is very simple. Every user of the class Foo expects that the string member stays stable (const) during the whole lifetime of an object of this class. Any unexpected change could lead to undefined behavior in the worst case, e.g., somebody saved the size of the string and doing an out of range access after somebody have applied the evil const_cast for change the value (and size!). Or, somebody saved the char pointer of the string which got invalid after the string value has been changed (re-allocation). Or, …
The Foo class is designed for to create a new instance if a new string value is needed. But here the design choice is completely ignored when using a const_cast
. So, please, don’t do this ever in your code!
Can const_cast be unevil?
So, is there then any unevil const_cast<>
???
Yes, there is. const_cast<>
can be used in a good way without any harm or damage for avoid code duplication.
A great use case for this I found during the implementation of the Collection
class from the TeaScript C++ Library.
The Collection class is a Container class with stable storage order, which implements a LIFO behavior and provides access by index as well as by a key (optionally). The complexity is comparable with std::vector
, but if keys are used removing elements others than the last will add some extra complexity on top due to maintaining the access by key.
The source code can be viewed here: Collection.hpp
The usage of an unevil const_cast<>
looks like this:
// code from https://github.com/Florian-Thake/TeaScript-Cpp-Library/blob/main/include/teascript/Collection.hpp
// [...]
class Collection
{
public:
// [...]
ValueType const &GetValueByKey( KeyType const &rKey ) const
{
auto it = mLookup.find( rKey );
if( it != mLookup.end() ) {
assert( ContainsIdx( it->second ) );
return GetValueByIdx_Unchecked( it->second );
}
throw exception::out_of_range( "Collection: Invalid key! Key not found!" );
}
ValueType & GetValueByKey( KeyType const &rKey )
{
// one of the rare unevil const_cast:
// first make this const to can re-use the const code,
// then remove the const again from result.
return const_cast<ValueType &>(const_cast<Collection const &>(*this).GetValueByKey( rKey ));
}
// [...]
};
Explanation
In this example I have 2 getters (as overload) for returning the value as a reference. The one is for the const case, if the class instance is called in a const context or the return value is used as a const reference. The other overload is only available in non-const contexts when the value is used as a mutable reference. The class instance must be non-const for this.
But for obtaining the return value some code must be executed first. So, do I need to use duplicate code here?
No, with the const_cast<>
in the non-const(!) overload the duplicate code can be avoided at all.
The non-const overload will only be called if the object is used in a non-const context (which implies a non-const object as well).
The trick here is, that we cast the object itself (the this pointer) to const, so that we create a const context for calling the const overload for reusing that code.
The const return value then can be safely cast (no harm, no danger here!) to non-const with the const_cast<>
because we are in a non-const context here already and the object is in fact non-const. We only made it artificially const which can be safely removed again.
With this the duplicate code can be avoided at all. 🙂
Conclusion / Final thoughts
Personally I think, this is a great usage example for avoid duplicate code in const and non-const method overloads.
What do you think of this technique and/or this concrete real-world example?
I hope you enjoyed reading this article and I will ‘see’ you again in part 3 of the series. Thank you for reading. 🙂