class foo {
std::vector<int>data;
public:
int operator[](int n) {
return data[n];
}
int operator[](int n) const {
return data[n];
}
};
Now in my program I do:
foo myFoo;
int x = myFoo[123];
...
Should the const version of foo::operator[] be called?
I think it should, but my compiler disagrees with me.
What's the correct behavior? Why...?
--
<\___/>
/ O O \
\_____/ FTB.
http://www.topaz3d.com/ - New 3D editor!
> I define this class:
>
>
> class foo {
> std::vector<int>data;
> public:
>
> int operator[](int n) {
> return data[n];
> }
> int operator[](int n) const {
> return data[n];
> }
> };
>
>
> Now in my program I do:
>
> foo myFoo;
> int x = myFoo[123];
> ...
>
>
> Should the const version of foo::operator[] be called?
>
> I think it should, but my compiler disagrees with me.
>
> What's the correct behavior? Why...?
I'm interested in why you think it should call the const version. If it
did, when would it ever call the non-const version?
No.
> I think it should, but my compiler disagrees with me.
The compiler's right. The const version should be called for a const
object. The non-const version should be called for a non-const object.
If you don't/didn't have a non-const version, then the const version
could be called -- but it would be called by convertion the non-const to
a const object first. That's fine (in this case) but it's still a
conversion. When matching overloaded functions, one that doesn't require
a conversion is a better match than one that does require a conversion.
Most people want the const version used on the RHS of an assignment, but
the non-const for the LHS. To get what that kind of behavior, you
normally use a proxy object that overloads operator= and operator T.
--
Later,
Jerry.
The universe is a figment of its own imagination.
Ok, maybe I oversimplified it. Supposed operator[]
returns a reference to the int:
int& operator[](int n) {
return data[n];
}
int& operator[](int n) const {
return data[n];
}
If the expression is on the RHS of an assignment, should
the const version be called?
...and just before the pedants arrive, suppose it's
a struct not an int, and I want to access a member
of the stuct.
my_struct& operator[](int n) { return data[n]; }
const my_struct& operator[](int n) const { return data[n]; }
Why does the compiler choose the non-const version
for the RHS of an expression...?
Changing this line to this:
return data.at(n);
is much safer
> }
> int operator[](int n) const {
> return data[n];
> }
> };
>
>
> Now in my program I do:
>
> foo myFoo;
> int x = myFoo[123];
> ...
>
This should call the non-const operator[]
If you had:
const foo myFoo;
int x = myFoo[123];
then the const version of the operator[] would be used
Because your myFoo object is not const
Bummer. I've got an object which triggers quite
a big internal rebuild when you call the non-const
version and I just noticed it's doing a lot of
rebuilding because of this assumption.
You can explicitly convert your object to a const version if you don't
want the non-const member function to be called in some cases:
const foo& myConstFoo = myFoo;
int blah = myConstFoo[42];
or something like that. static_cast<foo const&>(myFoo)[42] should also
work as far as I can tell. Though, these kinds of casts are still a
bit of a mystery to me.
Cheers,
SG
> On Oct 30, 10:21=A0am, fungus <openglMYSO...@artlum.com> wrote:
> >
> > Ok, maybe I oversimplified it. Supposed operator[]
> > returns a reference to the int:
>
> ...and just before the pedants arrive, suppose it's
> a struct not an int, and I want to access a member
> of the stuct.
>
> my_struct& operator[](int n) { return data[n]; }
> const my_struct& operator[](int n) const { return data[n]; }
>
> Why does the compiler choose the non-const version
> for the RHS of an expression...?
That's a better question. It chooses the non-const version because C++
doesn't overload based on return type or how the caller uses the return
value, only arguments to the function (including the implicit "this"
argument to member functions). I could have sworn "The Design and
Evolution of C++" covered the reason behind this, but I couldn't find a
reference. I'm assuming it would complicate overloading and often not be
desired.
> On 30 Okt., 10:59, fungus <openglMYSO...@artlum.com> wrote:
> > Bummer. I've got an object which triggers quite
> > a big internal rebuild when you call the non-const
> > version and I just noticed it's doing a lot of
> > rebuilding because of this assumption.
>
> You can explicitly convert your object to a const version if you don't
> want the non-const member function to be called in some cases:
>
> const foo& myConstFoo = myFoo;
> int blah = myConstFoo[42];
But you can't expect users of the class to do this consistently, since
it's quite tedious. Even having a named T& modify( int index ) would be
better. As others have mentioned, a proxy object with an operator T () and
operator = ( T const& ) would be most transparent to the user.
Maybe you mean section 3.7.1 ?
I don't see how that works for a struct...
I want operator[] to return a reference to a struct
and be able to do this:
Foo f = myStuff[i].foo;
and
myStuff[i].foo = f;
With the first one calling the const version of operator[].
> Bummer. I've got an object which triggers quite
> a big internal rebuild when you call the non-const
> version and I just noticed it's doing a lot of
> rebuilding because of this assumption.
Then either the two methods' semantics are too different for a
single identifier, or the entity making the offending calls shouldn't
get its hands on a non-const reference, or you should memoize the
rebuilding method.
Martin
--
Quidquid latine scriptum est, altum videtur.
This is probably the best solution. I need both const
and non const access inside an event handler function
so I guess I need to make the access more fine-grained.
> > As others have mentioned, a proxy object with an operator T
> > () and operator = ( T const& ) would be most transparent to
> > the user.
> I don't see how that works for a struct...
It doesn't, really.
> I want operator[] to return a reference to a struct and be
> able to do this:
> Foo f = myStuff[i].foo;
> and
> myStuff[i].foo = f;
> With the first one calling the const version of operator[].
There are several solutions. The best would be to find a
compiler modified to allow you to overload operator., and use
that. But that sort of ruins portability. Otherwise, you can
overload operator-> in the proxy, and tell your users to use
that whenever they do a []---it's ugly and counter-intuitive,
but it works. And for special cases, you can define all of the
members in the proxy, so that they forward to the correct member
of owning class. Or you can just require your clients to get a
reference first, e.g.:
Whatever const& rf = myStuff[ i ].foo
Foo f = rf.foo ;
--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> On Oct 30, 1:15 pm, fungus <openglMYSO...@artlum.com> wrote:
[...]
> > I want operator[] to return a reference to a struct and be
> > able to do this:
> >
> > Foo f = myStuff[i].foo;
> >
> > and
> >
> > myStuff[i].foo = f;
> >
> > With the first one calling the const version of operator[].
>
> [...] Or you can just require your clients to get a
> reference first, e.g.:
>
> Whatever const& rf = myStuff[ i ].foo
> Foo f = rf.foo ;
or
class Stuff {
public:
Whatever const& operator [] ( int ) const;
Whatever& modify( int );
};
void user( Stuff& myStuff )
{
Foo f = myStuff[ i ].foo;
// ...
myStuff.modify( i ).foo = f;
}
If modification is more common than reading, you could instead have [] be
the mutator, and a read( int ) be the accessor.
This (or variations on it) works too...
While I do remember this feeling, this case is rather simple:
You modify 'const', so a 'const_cast' would be what you should
use.
> Cheers,
> SG
Schobi
I'd be annoyed if I had to use such a 'foo'.
If I want the safe (and slower) variant, I'd want a
'foo::at()' to cal.
> [...]
Schobi