const correctness - should C++ prefer const member over non-const?

F

fungus

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::eek:perator[] 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!
 
J

Jerry Coffin

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::eek:perator[] be called?

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.
 
F

fungus

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?

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?


--
<\___/>
/ O O \
\_____/ FTB.

http://www.topaz3d.com/ - New 3D editor!
 
F

fungus

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...?


--
<\___/>
/ O O \
\_____/ FTB.

http://www.topaz3d.com/ - New 3D editor!
 
A

anon

fungus said:
I define this class:


class foo {
std::vector<int>data;
public:

int operator[](int n) {
return data[n];

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
Should the const version of foo::eek:perator[] be called?

I think it should, but my compiler disagrees with me.

What's the correct behavior? Why...?
 
A

anon

fungus said:
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...?

Because your myFoo object is not const
 
F

fungus

Should the const version of foo::eek:perator[] be called?
No.

I think it should, but my compiler disagrees with me.

The compiler's right.

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.


--
<\___/>
/ O O \
\_____/ FTB.

http://www.topaz3d.com/ - New 3D editor!
 
S

SG

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
 
F

fungus

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...

I want operator[] to return a reference to a struct
and be able to do this:

Foo f = myStuff.foo;

and

myStuff.foo = f;


With the first one calling the const version of operator[].



--
<\___/>
/ O O \
\_____/ FTB.

http://www.topaz3d.com/ - New 3D editor!
 
M

Martin Eisenberg

fungus said:
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
 
F

fungus

the entity making the offending calls shouldn't
get its hands on a non-const reference

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.


--
<\___/>
/ O O \
\_____/ FTB.

http://www.topaz3d.com/ - New 3D editor!
 
J

James Kanze

On Oct 30, 11:45 am, (e-mail address removed) (blargg) wrote:
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.foo;

myStuff.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 ;
 
F

fungus

    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;
    }

This (or variations on it) works too...


--
<\___/>
/ O O \
\_____/ FTB.

http://www.topaz3d.com/ - New 3D editor!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,734
Messages
2,569,441
Members
44,832
Latest member
GlennSmall

Latest Threads

Top