patterncppModerate
Avoiding code duplication and retaining const correctness
Viewed 0 times
duplicationandcorrectnessconstcodeavoidingretaining
Problem
Sometimes I run across the problem of having two member function overloads that only differ in the constness of
Most of the time these are one-liners, but even then, I hate the code duplication.
That's why I like to use (if possible) the
The first
How do you solve this problem?
this (and the return type, but that is less important):int& operator[](int);
const int& operator[](int)const;Most of the time these are one-liners, but even then, I hate the code duplication.
That's why I like to use (if possible) the
const version to implement the non const version via const_cast:const int& MyClass::operator[](int index)const {
return const_cast(const_cast(*this)[index]);
}The first
const_cast is required (at least I think so), but I wonder if the second one can be circumvented and if so, if the code is more readable then.How do you solve this problem?
Solution
First you have it the wrong way around.
You should not use the mutating version to supply data to the non-mutating function:
What happens if
If you had written it the other way around:
This is better. Because if you change the
But personally I prefer to add another helper method and thus remove all athe casting:
Answer to comment below:
I would not use it for const/non-const iterators
You should not use the mutating version to supply data to the non-mutating function:
const int& MyClass::operator[](int index)const {
return const_cast(const_cast(*this)[index]);
}What happens if
operator[] is modified and does mutate slightly the internal state? By casting away the constness you have opened up your class to undefined behavior. You should NEVER cast away constness. If you must you can use const_cast to add constness, but removing it is so dangerous that this should be looked at as breaking code.If you had written it the other way around:
int& MyClass::operator[](int index) {
return const_cast(const_cast(*this)[index]);
}This is better. Because if you change the
operator[] so that it actually mutates the class it will generate a compiler error and thus prevent you from accidentally getting undefined behavior. But personally I prefer to add another helper method and thus remove all athe casting:
private:
// NOTE: This is not a perfect solution for all situations.
// But it works for 99% of situations and keeps the code clean
// For the remaining situations switch back to use const cast
// **BUT** always add never remove constness from an object.
// Never make public as it is const and gives away an internal reference.
// Only use via the public API
int& get(int index) const
{
return stuff[index];
}
public:
// Keep the API simple/clean and DRY
// move all the work to private methods.
int& operator[](int index) { return get(index);}
int const& operator[](int index) const { return get(index);}Answer to comment below:
I would not use it for const/non-const iterators
const_iterator begin() const {return const_iterator();}
iterator begin() {return iterator();}Code Snippets
const int& MyClass::operator[](int index)const {
return const_cast<int&>(const_cast<MyClass&>(*this)[index]);
}int& MyClass::operator[](int index) {
return const_cast<int&>(const_cast<MyClass const&>(*this)[index]);
}private:
// NOTE: This is not a perfect solution for all situations.
// But it works for 99% of situations and keeps the code clean
// For the remaining situations switch back to use const cast
// **BUT** always add never remove constness from an object.
// Never make public as it is const and gives away an internal reference.
// Only use via the public API
int& get(int index) const
{
return stuff[index];
}
public:
// Keep the API simple/clean and DRY
// move all the work to private methods.
int& operator[](int index) { return get(index);}
int const& operator[](int index) const { return get(index);}const_iterator begin() const {return const_iterator(<X>);}
iterator begin() {return iterator(<X>);}Context
StackExchange Code Review Q#44460, answer score: 16
Revisions (0)
No revisions yet.