Background
The following code block appears in Scott Meyers' famous book "Effective C++" Item 3:
class TextBlock {
public:
...
const char& operator[](std::size_t position) const
{
... // do bounds checking
... // log access data
... // verify data integrity
return text[position];
}
char& operator[](std::size_t position)
{
... // do bounds checking
... // log access data
... // verify data integrity
return text[position];
}
...
private:
std::string text;
};
The author states that in the above implementation, the contents of the const
and the non-const
overloads are essentially the same. In order to avoid code duplication, it could be simplified like this:
class TextBlock {
public:
...
const char& operator[](std::size_t position) const // the same as before
{
...
...
...
return text[position];
}
char& operator[](std::size_t position) // now just calls const op[]
{
return // cast away const on
const_cast<char&>( // op[]'s return type;
static_cast<const TextBlock&>(*this) // add const to *this's type;
[position] // call const version of op[]
);
}
...
private:
std::string text;
};
Questions
My questions are:
When shall we need an overload for
const T&
and another forT&&
? (Here,T
may be a template parameter or a class type, soT&&
may or may not mean a universal reference) I can see that in the standard library, many classes provide both overloads. Examples are constructors ofstd::pair
andstd::tuple
, there are tons of overloads. (Okay, I know that among the functions, one of them is the copy constructor and one of them is the move constructor.)Is there a similar trick to share the implementations for the
const T&
andT&&
overloads? I mean, if theconst T&&
overload returns an object that is copy-constructed, and theT&&
overload returns something that is move-constructed, after sharing the implementation, this property must still hold. (Just like the above trick:const
returnsconst
and non-const
returns non-const
, both before and after implementation sharing)
Thanks!
Clarifications
The two overloads I'm referring to should look like:
Gadget f(Widget const& w);
Gadget f(Widget&& w);
It is nothing related to returning by rvalue references, that is:
Widget&& g(/* ... */);
(By the way, that question was addressed in my previous post)
In f()
above, if Gadget
is both copy-constructible and move-constructible, there's no way (except from reading the implementation) to tell whether the return value is copy-constructed or move-constructed. It is nothing to deal with Return Value Optimization (RVO) / Named Return Value Optimization (NRVO). (See my previous post)