473,508 Members | 2,360 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Dangling Reference. Idiom

Suppose that I have a method that returns references to "elements" in
an iterator, and a method to advance the iterator:

class Element { /* ... */ };

class Iterator {
public:
Element& operator*() const;

Iterator& operator++();

/* ... */
};
Now, I also want the clients of the iterator to know that the
reference to element returned by the operator* is only valid
temporarily. That is, clients of the iterator are not supposed to hold
the element reference beyond the next call to operator++, for it will
be dangling. See below:

Iterator iter(/* ... */);
Element &e = *iter;
/* Do something with e */
++iter; // At this point the reference e becomes dangling.
I would like to convey that semantic constraint to the client. A
comment would probably be sufficient. However, I would like to explore
the possibility of expressing the constraint in code.

It occurred to me to use and ancillary class to do so:

struct WillDangleRef { // Ancillary class
operator Element&() const;

private:
WillDangleRef(WillDangleRef const& ); // Hidden. Not Implemented

Element &element_;

WillDangleRef(Element& e): element_(e) {}
};
class Iterator {
public:
WillDangleRef operator*() const; // The operator* now returns
an object that
// can only be
used for conversions into Element&

Iterator& operator++();

/* ... */
};

The WillDangleRef temporary will be destructed right after the
statement that contains the call to the operator*, which gives a clue
as to the transient nature of the reference that it can be converted
into. That leaves clients with the only choice of using the reference
as a parameter to a function, which I do not like very much because I
feel is too restrictive:

void doSomethingWithE(Element& e);

Iterator iter(/* ... */);
doSomethingWithE(*iter);
++iter; // At this point the temporary returned by *iter is gone.
Could you please comment? Any other ideas?

Thanks:

Belebele

Feb 7 '07 #1
10 1724
Belebele wrote:
Suppose that I have a method that returns references to "elements" in
an iterator, and a method to advance the iterator:

class Element { /* ... */ };

class Iterator {
public:
Element& operator*() const;

Iterator& operator++();

/* ... */
};
Now, I also want the clients of the iterator to know that the
reference to element returned by the operator* is only valid
temporarily. That is, clients of the iterator are not supposed to hold
the element reference beyond the next call to operator++, for it will
be dangling. See below:

Iterator iter(/* ... */);
Element &e = *iter;
/* Do something with e */
++iter; // At this point the reference e becomes dangling.
I would like to convey that semantic constraint to the client. A
comment would probably be sufficient. However, I would like to explore
the possibility of expressing the constraint in code.

It occurred to me to use and ancillary class to do so:

struct WillDangleRef { // Ancillary class
operator Element&() const;

private:
WillDangleRef(WillDangleRef const& ); // Hidden. Not Implemented

Element &element_;

WillDangleRef(Element& e): element_(e) {}
};
class Iterator {
public:
WillDangleRef operator*() const; // The operator* now returns
an object that
// can only be
used for conversions into Element&

Iterator& operator++();

/* ... */
};

The WillDangleRef temporary will be destructed right after the
statement that contains the call to the operator*, which gives a clue
as to the transient nature of the reference that it can be converted
into. That leaves clients with the only choice of using the reference
as a parameter to a function, which I do not like very much because I
feel is too restrictive:

void doSomethingWithE(Element& e);

Iterator iter(/* ... */);
doSomethingWithE(*iter);
++iter; // At this point the temporary returned by *iter is gone.
Could you please comment? Any other ideas?
A BAD IDEA(tm). Iterator should not be the owner of the object to
which gives access. Iterator should only provide a way to refer to
some particular element _elsewhere_ (like a container). If the object
to which the iterator refers does not survive after the iterator is
incremented, you shouldn't return a reference, you should probably
return a copy of the object (i.e. return by value).

As to your "WillDangleRef", what prevents me from doing

WillDangleRef const& ref = *it; ++it;

? Will 'ref' survive? It should, I bound a reference to it. Now,
right after the next statement whatever 'ref' allows me to access
is still invalid, isn't it?

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Feb 7 '07 #2
A BAD IDEA(tm). Iterator should not be the owner of the object to
which gives access.
I would prefer if we steer the discussion away from the fact that I
named my class Iterator. I get the impression that you are using your
preconceived ideas about iterators to shape the discussion. My
intention is to discuss the method that returns a temporary reference
which will become dangling according to some rule.

However, I would like to discuss that concept of the iterator, but as
a separate topic, once we find some common ground with the topic of
the reference.
As to your "WillDangleRef", what prevents me from doing

WillDangleRef const& ref = *it; ++it;

? Will 'ref' survive? It should, I bound a reference to it. Now,
right after the next statement whatever 'ref' allows me to access
is still invalid, isn't it?
My understanding is that the temporary WillDangleRef object will be
destroyed as soon as the statement where it is created completes
execution. In this case, that statement is:

WillDangleRef const& ref = *it;

Thus, the reference ref will become dangling immediately. Using ref
will cause undefined behavior. My point in creating the auxiliary
class was to convey to clients (who are aware of the life-time rules
of temporaries) that the element reference provided by calling the
WillDangleRef object's 'operator Element&' is invalid right after the
statement that created the WillDangleRef object.

Feb 7 '07 #3
Victor Bazarov wrote:
>
A BAD IDEA(tm). Iterator should not be the owner of the object to
which gives access.
Just one qualification: that's sometimes how input iterators works,
which is why there are so many restrictions on what you can do with an
input iterator. But that sort of thing should definitely be avoided.
And, in the case of input iterators, operator* returns a value, not a
reference, so you can hang on to its result regardless of what you
subsequently do with the iterator.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
Feb 7 '07 #4
Belebele wrote:
>A BAD IDEA(tm). Iterator should not be the owner of the object to
which gives access.

I would prefer if we steer the discussion away from the fact that I
named my class Iterator. I get the impression that you are using your
preconceived ideas about iterators to shape the discussion.
I am not shaping the discussion. And, yes, I have plenty of "my
preconceived ideas" about many things in this world. That's what is
known as "experience". If you want to break out of the mold while
reusing terms bound to that mold, perhaps you should have established
the terminology first...
My
intention is to discuss the method that returns a temporary reference
which will become dangling according to some rule.
Fine. Then you should have used a neutral name for your type, like
'Foo'. You invited the criticism about the "Iterator" by using that
term.
However, I would like to discuss that concept of the iterator, but as
a separate topic, once we find some common ground with the topic of
the reference.
Please, by all means.
>As to your "WillDangleRef", what prevents me from doing

WillDangleRef const& ref = *it; ++it;

? Will 'ref' survive? It should, I bound a reference to it. Now,
right after the next statement whatever 'ref' allows me to access
is still invalid, isn't it?

My understanding is that the temporary WillDangleRef object will be
destroyed as soon as the statement where it is created completes
execution.
No, it will not. It will be destroyed at the same time 'ref' (the
reference bound to it) is destroyed. If you would like to experiment,
here is some code:

#include <iostream>
struct dtor_outputs {
~dtor_outputs() { std::cout << "~dtor_outputs\n"; }
};
dtor_outputs foo() {
return dtor_outputs();
}
int main() {
dtor_outputs const& ref = foo();
std::cout << "Belebele thinks this will be after d-tor\n";
std::cout << "Bazarov thinks this will be before d-tor\n";
}

, see what you get.
In this case, that statement is:

WillDangleRef const& ref = *it;

Thus, the reference ref will become dangling immediately. Using ref
will cause undefined behavior. My point in creating the auxiliary
class was to convey to clients (who are aware of the life-time rules
of temporaries) that the element reference provided by calling the
WillDangleRef object's 'operator Element&' is invalid right after the
statement that created the WillDangleRef object.
I *got* your point. My point is that even with that class you cannot
protect the reference against becoming invalid. That's why you're
better off not confusing your users but simply documenting the existing
behaviour.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Feb 7 '07 #5
My
intention is to discuss the method that returns a temporary reference
which will become dangling according to some rule.

Fine. Then you should have used a neutral name for your type, like
'Foo'. You invited the criticism about the "Iterator" by using that
term.
I apologize for choosing a rather misleading class name. Names are so
important ...
My understanding is that the temporary WillDangleRef object will be
destroyed as soon as the statement where it is created completes
execution.

No, it will not. It will be destroyed at the same time 'ref' (the
reference bound to it) is destroyed. If you would like to experiment,
here is some code:

#include <iostream>
struct dtor_outputs {
~dtor_outputs() { std::cout << "~dtor_outputs\n"; }
};
dtor_outputs foo() {
return dtor_outputs();
}
int main() {
dtor_outputs const& ref = foo();
std::cout << "Belebele thinks this will be after d-tor\n";
std::cout << "Bazarov thinks this will be before d-tor\n";
}

, see what you get.
Right on the money. Now, is that behavior specified by the standard,
or is it compiler-specific?
However, I would like to discuss that concept of the iterator, but as
a separate topic, once we find some common ground with the topic of
the reference.

Please, by all means.
Let me put my ideas together. I will start another topic related to
this and I would be delighted to discuss with you a couple of design
ideas that involve iterators for an abstraction layer here at work.

Once again, thanks.

Feb 7 '07 #6
Belebele wrote:
I would prefer if we steer the discussion away from the fact that I
named my class Iterator. I get the impression that you are using your
preconceived ideas about iterators to shape the discussion.
Most people here has the preconceived idea that people choose names that
does not increment the noise level in the discussion.

--
Salu2
Feb 7 '07 #7
Belebele wrote:
That is, clients of the iterator are not supposed to hold
the element reference beyond the next call to operator++, for it will
be dangling. See below:

Iterator iter(/* ... */);
Element &e = *iter;
Simple, don't supply this interface. Don't let them dereference the
iterator with *. Then they can only use -and the problem is solved.

Of course, your "iterator" won't work as an iterator but names aren't
important so...
Feb 7 '07 #8
Belebele wrote:
[..]
I wrote:
> If you would like to
experiment, here is some code:

#include <iostream>
struct dtor_outputs {
~dtor_outputs() { std::cout << "~dtor_outputs\n"; }
};
dtor_outputs foo() {
return dtor_outputs();
}
int main() {
dtor_outputs const& ref = foo();
std::cout << "Belebele thinks this will be after d-tor\n";
std::cout << "Bazarov thinks this will be before d-tor\n";
}

, see what you get.

Right on the money. Now, is that behavior specified by the standard,
or is it compiler-specific?
It is specified by the Standard.

Binding a reference to const in order to prolong the life of some
temporary obtained from an expression is not a common idiom. It
is not unheard of, though. Of course, even in the program above,
you're likely to see

dtor_outputs obj = foo();

rather than what I wrote. The compiler is allowed to eliminated
extraneous temporary objects when constructing 'obj'.

And since constructing an object is not necessarily expensive, you
might want to actually make your class return an object by value
instead of a reference, thus eliminating the lifetime issue fully.
I think I even saw Pete Becker suggest the same thing.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Feb 7 '07 #9
Victor Bazarov wrote:
>
And since constructing an object is not necessarily expensive, you
might want to actually make your class return an object by value
instead of a reference, thus eliminating the lifetime issue fully.
I think I even saw Pete Becker suggest the same thing.
Yes, but that was back when I thought iterator meant iterator. <gOne
of the underlying assumptions of the STL and, hence, the design of
iterators and algorithms, is that the objects that these things deal
with are cheap to create and to copy. Dealing with heavy objects often
means allocating them on the heap and managing them through smart
pointers, which also probably makes the original code cleaner.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
Feb 7 '07 #10

Belebele wrote:
>
Now, I also want the clients of the iterator to know that the
reference to element returned by the operator* is only valid
temporarily.
Could you please comment? Any other ideas?
I think client must assuming that if iterator has changed " ++it " all data
accessable via the iterator or cached by the iterator is has changed also.
The simplest kind of iterator is counter

char buf[10];
int it=0;
&buf[it] != &but[it+1];

If client walking over container, client must use data befor iterator
changed, or use several iterators (they are designed for it) or make copyes
of data

const Element tmp1(*it); ++it;
const Element tmp2(*it); ++it;

--
Maksim A. Polyanin

"In thi world of fairy tales rolls are liked olso"
/Gnume/
Feb 8 '07 #11

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

13
3064
by: Aravind | last post by:
I would like to know in what manner dangling pointers affect the security of a application developed using C++.What are the loopholes that are created by dangling pointers and how they could be...
11
1911
by: John | last post by:
Hi: Below is a simple code: class link1 { public: link1(); link1(int &b1, double &b2); int* a1;
6
1944
by: Tony Johansson | last post by:
Hello Experts! I'm reading in a book about C++ and the book says common errors is "A function should not return a constant references to its parameter passed by constant references" Why? Here...
6
2074
by: Matthias Kaeppler | last post by:
Hi, I have a question regarding references, and their chance to "dangle" (if that's possible at all): Say I have a collection ob objects, and I take a reference to one of them. Now I sort...
20
6525
by: __PPS__ | last post by:
Hello everybody in a quiz I had a question about dangling pointer: "What a dangling pointer is and the danger of using it" My answer was: "dangling pointer is a pointer that points to some...
275
12037
by: Astley Le Jasper | last post by:
Sorry for the numpty question ... How do you find the reference name of an object? So if i have this bob = modulename.objectname() how do i find that the name is 'bob'
0
7223
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
7321
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
7377
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
1
7036
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
7489
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
1
5047
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...
0
3191
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
1547
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated ...
1
762
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.