"cppaddict" <> wrote in message
news:...
> Hi,
>
> Is it considered bad form to have the subscript operator return a
> const reference variable? If not, what is the proper way to do it?
> My question was prompted by the code below, my problematic attempt to
> implement a subscript operator that returns a const reference. The
> dubious code is marked at the end.
>
> <code>
>
> //MyClass is a large class we don't want to copy alot
> //MyClass has a "char getChar()" function
> class MyClass;
>
> class SubscriptTest {
> private:
> std::vector<MyClass> _vect;
> public:
> SubscriptTest() {};
> void addObj(MyClass mc) {_vect.push_back(mc);}
> const MyClass& operator[] (char ch) const {return
> lookUpByChar(ch);}
> };
>
> const MyClass& SubscriptTest::lookUpByChar(char ch) const {
> for (unsigned i=0; i<_vect.size(); i++) {
> if (_vect[i].getChar() == ch)
> return i;
> }
>
> //PROBLEM IS HERE
> return *(new MyClass); //return value here needed to compile
> }
>
> </code>
>
> The problem is what to return when the lookup fails. Creating a
> default value via new, as I do above, seems wrong -- it wastes memory
> and requires tracking the objects created and then destroying them in
> Subscript's destructor.
>
> Another option would be to create the default value as static variable
> inside the lookUpByChar function or as a member variable of
> SubscriptTest, and have the function always return a reference to
> that. That seems reasonable to me, but I still wasn't sure.
>
> Does anyone have any thoughts/suggestions?
>
> Thanks,
> cpp
>
CPP,
Returning a const reference is not only good form, but is the right thing to
do if the
circumstances demand it. In the situation where you are invoking the
subscript operator
on a const object, you might want to return a const reference. In fact, you
probably need
two implementations, non-const and const so you can modify values and just
read them
respectively.
For example, in this example, the second call to operator[] uses a const
reference since s2 is a const object.
If you step through this code in the debugger, you will see the correct flow
through both the const and non-const operator[], and the appropriate
operator[] for the
element access within the _data vectors of MyString.
#include <vector>
#include <iostream>
using namespace std;
class MyString {
public:
MyString( char * data )
{ _data.assign( data, data+strlen(data)+1); }
char& operator[](int position) { return _data[position]; }
const char& operator[](int position) const { return _data[position]; }
private:
vector<char> _data;
};
int main()
{
MyString s1 = "Foo";
cout << s1[0]; // calls non-const MyString:

perator[]
const MyString s2 = "Bar";
cout << s2[0]; // calls const MyString:

perator[]
return 0;
}
I have a second point. In your posting, you say that the Myclass objects
are expensive/large to
create and so you don't want to create them unnecessarily. If your
operator[] returns references,
then you do not have to create any new objects, you can do something like
this:
class Dictionary
{
private:
vector<MyString> _words;
public:
Dictionary() {}
~Dictionary() {}
MyString& operator[]( int i )
{
int x = 0;
for ( vector<MyString>::iterator it=_words.begin(); it!=_words.end();
++it, ++x)
{
if ( i == x)
{
return *it;
}
}
// throw - you should never reach here...
// return statement here to defeat compiler error/ warning.
return _words[0];
}
const MyString& operator[] (int i ) const
{
int x =0;
for ( vector<MyString>::const_iterator cit=_words.begin();
cit!=_words.end(); ++cit, ++x)
{
if ( i == x)
{
return *cit;
}
}
// throw - we should not ever get here if i is a valid index. ....
// return statement here to defeat compiler error/warning.
return _words[0];
}
};
this example is a bit contrived, but it meant to illustrate the point that
you can avoid the creation
of new objects by returning references to existing ones in _words vector,
either the one you
are looking for _words[i] or a default value _words[0] in case of failure.
This satisfies the
need for the compiler to find a return through all branches of the function
and stylistic needs too.
Finally, the operator[] should throw an exception in the case of an invalid
indexing value ( well
at least in the "read" case), there is not much else it can reasonably do!
It is not a reasonable interpretation to use operator[] to do lookup of an
element, that should be another function with a different interface suitable
for the task.
dave