returning references

D

Daniel T.

Daniel said:
Really, could you give an example? Maybe I'm mistaken...

std::map< int, int > the_map;
...
the_map[5] = 6; // the_map[5] returns an int&.

I conjecture that the two of you are in disagreement about whether
that is an "internal state" of the map. Since I have no idea what
"internal state" is supposed to mean with regard to std::map, I
will not offer an opinion.

Right, that is not internal state, I define "internal state" as state
that is part of the invariant of the class. Does changing the state of
the object returned by map::eek:perator[] have any chance of breaking any
of the map's invariants? No.

Note, that the map class doesn't return a non-const reference from the
"size()" member-function. It could return either by value or by const
reference, either would be safe (assuming appropriate internal changes,)
and switching back in forth between them would not affect code that uses
map::size() in the slightest. (If it *did* affect code that uses the
member-function, the it is the calling code that is messed up.)
[What] if the client takes the address of [a const-reference]
return, and later uses it.

When James said the above, I took it to mean something like the example
below.

class Foo {
public:
const Bar& get() const;
};

void bad_client( Foo& foo ) {
const Bar* b = &foo.get();
// then do things, assuming that 'b' will continue to be
// valid no matter what I may do to 'foo'.
}

I consider such code to be inherently dangerous... When what is returned
is a const-reference to internal state, the calling code shouldn't
assume 'b's validity even past the ';' of the get call.
 
M

Michael DOUBEZ

Daniel T. a écrit :
Daniel said:
A map does not return a non-const reference to any internal
state, so I don't see how that relates in any way.
std::map certainly does return references to internal state, as
does every other map I've every seen or heard of.
Really, could you give an example? Maybe I'm mistaken...
std::map< int, int > the_map;
...
the_map[5] = 6; // the_map[5] returns an int&.

I conjecture that the two of you are in disagreement about whether
that is an "internal state" of the map. Since I have no idea what
"internal state" is supposed to mean with regard to std::map, I
will not offer an opinion.

Right, that is not internal state, I define "internal state" as state
that is part of the invariant of the class. Does changing the state of
the object returned by map::eek:perator[] have any chance of breaking any
of the map's invariants? No.

Since the lifetime of the object is managed by map<>, the returned value
references an internal state and it is clearly specified which
operations invalidate such objects: a call to clear() by example or
possibly a push_back in the case of vector said:
Note, that the map class doesn't return a non-const reference from the
"size()" member-function. It could return either by value or by const
reference, either would be safe (assuming appropriate internal changes,)
and switching back in forth between them would not affect code that uses
map::size() in the slightest. (If it *did* affect code that uses the
member-function, the it is the calling code that is messed up.)

Technically, both are posible but a reference semantic would be counter
intuitive on a state you cannot change or share reliably.
[What] if the client takes the address of [a const-reference]
return, and later uses it.

When James said the above, I took it to mean something like the example
below.

class Foo {
public:
const Bar& get() const;
};

void bad_client( Foo& foo ) {
const Bar* b = &foo.get();
// then do things, assuming that 'b' will continue to be
// valid no matter what I may do to 'foo'.
}

I consider such code to be inherently dangerous... When what is returned
is a const-reference to internal state, the calling code shouldn't
assume 'b's validity even past the ';' of the get call.


That depends on the contract you have with Foo. If it is a singleton or
a scoped_ptr<> by example, there is nothing wrong.

Michael
 
J

James Kanze

Daniel said:
A map does not return a non-const reference to any internal
state, so I don't see how that relates in any way.
std::map certainly does return references to internal state, as
does every other map I've every seen or heard of.
Really, could you give an example? Maybe I'm mistaken...
std::map< int, int > the_map;
...
the_map[5] = 6; // the_map[5] returns an int&.
I conjecture that the two of you are in disagreement about whether
that is an "internal state" of the map. Since I have no idea what
"internal state" is supposed to mean with regard to std::map, I
will not offer an opinion.
Right, that is not internal state, I define "internal state"
as state that is part of the invariant of the class. Does
changing the state of the object returned by map::eek:perator[]
have any chance of breaking any of the map's invariants? No.

But the fact that the element exists, in the map, is part of the
maps internal state. You can't change this via the reference,
but you can certainly change it via other functions, while the
reference is still present. E.g.:

std::map< int, int > theMap ;
int& r = theMap[ 5 ] ;
theMap.erase( 5 ) ;
// ...

The presence or absense of the element in the map *is* part of
the map's internal state. And a reference to the element is
thus also part of the map's internal state, since it designates
something under control of the map.
Note, that the map class doesn't return a non-const reference
from the "size()" member-function. It could return either by
value or by const reference, either would be safe (assuming
appropriate internal changes,) and switching back in forth
between them would not affect code that uses map::size() in
the slightest. (If it *did* affect code that uses the
member-function, the it is the calling code that is messed
up.)
[What] if the client takes the address of [a const-reference]
return, and later uses it.
When James said the above, I took it to mean something like the example
below.
class Foo {
public:
const Bar& get() const;

};
void bad_client( Foo& foo ) {
const Bar* b = &foo.get();
// then do things, assuming that 'b' will continue to be
// valid no matter what I may do to 'foo'.
}

That's more or less it. A more typical example might be a map
of external identifiers to entity objects. Somewhere, you need
a function which looks up the object, and returns a pointer to
it. And you don't want to call that function every single
access to the object.

I suppose that one could argue that the "internal state" of the
map here is only the pointer, and not the entity object itself,
because what the std::map will hold will be a pointer, but that
really doesn't change anything at the conceptual level. At the
conceptual level, the "internal state" of the map is your data
base, and you certainly do want functions which return pointers
to that.
I consider such code to be inherently dangerous... When what
is returned is a const-reference to internal state, the
calling code shouldn't assume 'b's validity even past the ';'
of the get call.

So you're saying that if I have a set of entity objects, I
should be required to do lookup in every expression which
accesses one of the objects. Internally, of course, it would be
an std::set< MyType* > (since MyType isn't copiable). But the
comparison function would dereference the pointer, so the values
in MyType would definitely be involved in the class invariants
of std::set.
 
J

James Kanze

Daniel T. a écrit :

[...]
That depends on the contract you have with Foo. If it is a
singleton or a scoped_ptr<> by example, there is nothing
wrong.

More generally: part of the problem may be just one of
semantics: what is meant by "internal state". But my fealing is
that the statement as presented by Daniel T is an over
simplification---the issues are just too complicated to be
covered by simple cliches. I'd be interesting in hearing his
comments on the following (which is a very frequent idiom in
servers in business applications):

class EntityObject : private boost::uncopiable
{
public:
virtual ~EntityObject() {}
virtual std::string id() const = 0 ;
// ...
} ;

class EntityObjectDataBase
{
private:
struct EntityObjectOrder
{
bool operator()(
EntityObject* lhs,
EntityObject* rhs ) const
{
return lhs->id() < rhs->id() ;
}
}
typedef std::set< EntityObject*, EntityObjectOrder >
DataBase ;
DataBase myData ;

public:
EntityObject* get( std::string const& id ) const
{
DataBase::const_iterator
entry = myData.find( id ) ;
return entry != myData.end()
? *entry
: NULL ;
}
// functions for insertion and removal...
} ;

Note that the object pointed to by the return value of
EntityObjectDataBase::get() definitely contains state relevant
to the class invariants of the myData member of
EntityObjectDataBase; presumable, one could even write a derived
EntityObject in which non-const member functions modified the
value returned by EntityObject::id(). Obviously, of course, one
doesn't want to do that, but there's nothing in the language
which would prevent it. The problem here is simple: the
language (and simple additional rules) can only go so far in
protecting against stupid errors. The requirement that I call
get() (and check for null) in every single statement which uses
an object is simply not tenable, and saving the pointer returned
by get() is seems to be a case of the sort of thing Daniel T
seems to be saying one should never do.
 
D

Daniel T.

James Kanze said:
Daniel T. said:
Daniel T. wrote:

A map does not return a non-const reference to any internal
state, so I don't see how that relates in any way.

std::map certainly does return references to internal state, as
does every other map I've every seen or heard of.

Really, could you give an example? Maybe I'm mistaken...

std::map< int, int > the_map;
...
the_map[5] = 6; // the_map[5] returns an int&.

I conjecture that the two of you are in disagreement about whether
that is an "internal state" of the map. Since I have no idea what
"internal state" is supposed to mean with regard to std::map, I
will not offer an opinion.

Right, that is not internal state, I define "internal state"
as state that is part of the invariant of the class. Does
changing the state of the object returned by map::eek:perator[]
have any chance of breaking any of the map's invariants? No.

I'm willing to drop the whole thing, it's my style, but I still want to
cover the issue you bring up below.
But the fact that the element exists, in the map, is part of the
maps internal state. You can't change this via the reference,
but you can certainly change it via other functions, while the
reference is still present.

The key to the above sentence is "you". *You* (i.e., the client using
the map) can "change it", the map cannot do so itself. In fact, the map
is specifically forbidden by the interface to remove/add an object
unless directed by certain special functions, and specifically required
to remove/add one when directed. The fact that the elements exist are
not "internal state".
E.g.:

std::map< int, int > theMap ;
int& r = theMap[ 5 ] ;
theMap.erase( 5 ) ;
// ...

The presence or absense of the element in the map *is* part of
the map's internal state. And a reference to the element is
thus also part of the map's internal state, since it designates
something under control of the map.

I don't agree. The map has no control over when the client will call
erase, and it must do the erasure when the client does call the
member-function. The map doesn't control the lifetime of the object
anymore than your bank controls what you do with your money.
 
D

Daniel T.

Michael DOUBEZ said:
Daniel T. a écrit :
Daniel T. wrote:
A map does not return a non-const reference to any internal
state, so I don't see how that relates in any way.
std::map certainly does return references to internal state, as
does every other map I've every seen or heard of.
Really, could you give an example? Maybe I'm mistaken...
std::map< int, int > the_map;
...
the_map[5] = 6; // the_map[5] returns an int&.

I conjecture that the two of you are in disagreement about whether
that is an "internal state" of the map. Since I have no idea what
"internal state" is supposed to mean with regard to std::map, I
will not offer an opinion.

Right, that is not internal state, I define "internal state" as state
that is part of the invariant of the class. Does changing the state of
the object returned by map::eek:perator[] have any chance of breaking any
of the map's invariants? No.

Since the lifetime of the object is managed by map<>, the returned value
references an internal state and it is clearly specified which
operations invalidate such objects: a call to clear() by example or
possibly a push_back in the case of vector<>.

Covered in my reply to James... 'vector' is an interesting case. The
vector is required to not invalidate the reference if at the time of the
call capacity() > size(). If that is not the case, then vector has some
control over the lifetime of the object referenced.
Technically, both are posible but a reference semantic would be counter
intuitive on a state you cannot change or share reliably.

As I said to James, this is a style issue. To me, returning by const
reference is a low level optimization over returning by object. So for
example, if 'size()' returned an object that was very expensive to copy,
I would use a const reference (or pointer to const) return rather than a
by value return and I would fully expect that clients of the class treat
it as if it was a by value return anyway.
[What] if the client takes the address of [a const-reference]
return, and later uses it.

When James said the above, I took it to mean something like the example
below.

class Foo {
public:
const Bar& get() const;
};

void bad_client( Foo& foo ) {
const Bar* b = &foo.get();
// then do things, assuming that 'b' will continue to be
// valid no matter what I may do to 'foo'.
}

I consider such code to be inherently dangerous... When what is returned
is a const-reference to internal state, the calling code shouldn't
assume 'b's validity even past the ';' of the get call.

That depends on the contract you have with Foo. If it is a singleton or
a scoped_ptr<> by example, there is nothing wrong.

Of course. The contract specifies control.
 
O

Old Wolf

Below is posted from a link for Stanford students in computer
science.

QUOTE BEGINS HERE
Because of the risk of misuse, some experts recommend never returning
a
reference from a function or method.
QUOTE ENDS HERE

This is obvious garbage; for example it is sensible to write:
ostream & operator<<( ostream &, YourClass const &y );

(Not to mention misuse of 'method' and 'function' terminology).
 

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

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top