struct BananaHolder
{
vector<Banana>& getBananas();
const vector<Banana>& getBananas() const;
};
My classes are cluttered with this kind of duplication.
Is there a cleaner, more elegant alternative?
struct BananaHolder
{
vector<Banana>& getBananas();
const vector<Banana>& getBananas() const;
};
My classes are cluttered with this kind of duplication.
Is there a cleaner, more elegant alternative?
If your class has to return references, and if it has to return a reference to a modifiable vector when invoked on a modifiable object, then I do not think there is a way to avoid the duplication - at least, not the duplicate declaration.
Notice, that some types of the Standard Library also suffer from the same problem. For instance, sequence containers such as std::vector
have both a const
and a non-const
overload of operator []
, at()
, back()
, front()
, data()
, and so on.
In C++23, you can solve this issue with explicit object parameters (aka deducing this):
struct BananaHolder
{
vector<Banana> m_bananas;
// using abbreviated function templates
auto& getBananas(this auto&& self) {
return self.m_bananas;
}
// or more verbosely
template <typename Self>
auto& getBananas(this Self&& self) {
return self.m_bananas;
}
};
This not only covers const BananaHolder
and BananaHolder
, it covers all combinations of const
, volatile
, lvalue reference, and rvalue reference qualifications.
If you wanted to make full use of this, you would write:
// to get the same behavior without templates, we would need 16 overloads
template <typename Self>
auto&& getBananas(this Self&& self) {
// without std::forward, this would always return an lvalue reference
return std::forward<Self>(self).m_bananas;
}
Prior to C++23, there is no way to avoid having at least two member functions. You would have to write two getter overloads like you did, for example.
For something more complicated than a getter, see How do I remove code duplication between similar const and non-const member functions?.