C++17 has updated the best answer for this question:
T const & f() const {
return something_complicated();
}
T & f() {
return const_cast<T &>(std::as_const(*this).f());
}
This has the advantages that it:
- Is obvious what is going on
- Has minimal code overhead — it fits in a single line
- Is hard to get wrong (can only cast away
volatile
by accident, butvolatile
is a rare qualifier)
If you want to go the full deduction route then that can be accomplished by having a helper function
template<typename T>
constexpr T & as_mutable(T const & value) noexcept {
return const_cast<T &>(value);
}
template<typename T>
constexpr T * as_mutable(T const * value) noexcept {
return const_cast<T *>(value);
}
template<typename T>
constexpr T * as_mutable(T * value) noexcept {
return value;
}
template<typename T>
void as_mutable(T const &&) = delete;
Now you can’t even mess up volatile
, and the usage looks like
decltype(auto) f() const {
return something_complicated();
}
decltype(auto) f() {
return as_mutable(std::as_const(*this).f());
}