an iterator question

J

Jess

Hello,

I learned that if we do "v.end()", then the returned iterator is a
temporary object and hence cannot be changed like

--v.end();

Why is the returned iterator a temporary pointer? I think when the
result of a function is returned, then a non-temporary object is
created, with value copied from the local temporary object. If the
code above still gets us a temporary object, then I think the non-temp-
object-creation hasn't taken place yet. Does it only happen if we do

p = v.end();

ie. assign v.end() to some variable?

It is also said that we can't modify it because it has a built in
type. I guess "build-in" means pointer type here. This sounds to me
as if we could modify the temporary object if it is of class type.
I'm quite confused....

Thanks,
Jess
 
V

Victor Bazarov

Jess said:
Hello,

I learned that if we do "v.end()", then the returned iterator is a
temporary object and hence cannot be changed like

--v.end();

Why? It can. If an object is *temporary* it does NOT mean that you
cannot change it. The problem here is that the value is effectively
lost right after you change it, unless you store it somewhere, like

it = --v.end();

Try this program in "debug" mode:
----------
#include <list>
#include <cassert>

int main()
{
std::list<int> li;
li.push_back(1);
li.push_back(2);
std::list<int>::iterator it = --li.end();
assert(*it == 2);
}
----------
Does the assertion fail?
Why is the returned iterator a temporary pointer?

A temporary pointer? WHAT???
I think when the
result of a function is returned, then a non-temporary object is
created, with value copied from the local temporary object. If the
code above still gets us a temporary object, then I think the
non-temp- object-creation hasn't taken place yet. Does it only
happen if we do

p = v.end();

ie. assign v.end() to some variable?

Huh? You lost me.
It is also said that we can't modify it because it has a built in
type.

"It is also said" *where*?? What is it that you're reading that is
so wrong?
I guess "build-in" means pointer type here. This sounds to me
as if we could modify the temporary object if it is of class type.
I'm quite confused....

It makes two of us.

V
 
P

Pete Becker

Victor said:
Why? It can. If an object is *temporary* it does NOT mean that you
cannot change it. The problem here is that the value is effectively
lost right after you change it, unless you store it somewhere, like

it = --v.end();

Depending on how the iterator's operator-- is written, it also might not
be legal. The subtlety here is that you can call non-const member
functions on a temporary object, but you cannot pass a temporary object
to a function through a non-const reference. The standard doesn't say
whether operator-- is a member function, just that --iter is valid and
has the appopriate effects.

struct iter1
{
iter1& operator--();
};
iter1 get1();

struct iter2
{
};
iter2& operator--(iter2&);
iter2 get2();

void f()
{
iter1 it1 = --get1(); // okay: calls iter1::eek:perator--
iter2 it2 = --get2(); // error: can't call operator--(iter2&)
}

--

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

Victor Bazarov

Pete said:
Depending on how the iterator's operator-- is written, it also might
not be legal. The subtlety here is that you can call non-const member
functions on a temporary object, but you cannot pass a temporary
object to a function through a non-const reference. The standard
doesn't say whether operator-- is a member function, just that --iter
is valid and has the appopriate effects.

struct iter1
{
iter1& operator--();
};
iter1 get1();

struct iter2
{
};
iter2& operator--(iter2&);
iter2 get2();

void f()
{
iter1 it1 = --get1(); // okay: calls iter1::eek:perator--
iter2 it2 = --get2(); // error: can't call operator--(iter2&)
}

Right. Thanks for clarifying that. BTW, in the implementation of
the Standard library Dinkumware publishes, any of the iterators for
the standard containers have their operator--() as non-member? Just
curious.

V
 
P

Pete Becker

Victor said:
Right. Thanks for clarifying that. BTW, in the implementation of
the Standard library Dinkumware publishes, any of the iterators for
the standard containers have their operator--() as non-member? Just
curious.

A quick grep suggests that the answer is no, but I haven't been in that
code for a year or more.

--

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

James Kanze

I learned that if we do "v.end()", then the returned iterator is a
temporary object and hence cannot be changed like
--v.end();

It depends on the type of the iterator. The above will work
with most iterators (because operator--() is a member function,
and you can call member functions on a temporary), but not with
all (because an iterator can be just a pointer, and the built-in
operator -- doesn't work on temporaries).
Why is the returned iterator a temporary pointer? I think when the
result of a function is returned, then a non-temporary object is
created, with value copied from the local temporary object. If the
code above still gets us a temporary object, then I think the non-temp-
object-creation hasn't taken place yet.

I'm not sure I follow what you're trying to say. The return
value of a function is always a temporary. If you use the
function to initialize an object, the non-temporary object is
initialized by copying this temporary. (In specific cases, the
compiler is allowed to suppress the additional object, as an
optimization. Don't worry about this for now.)
Does it only happen if we do
p = v.end();
ie. assign v.end() to some variable?

Roughly speaking, named objects are not temporaries, unnamed
objects are. If you write:

iterator p = v.end() ;

p is not a temporary. The results of v.end() are, and the
non-temporary p is initialized with a copy of this temporary.
It is also said that we can't modify it because it has a built in
type. I guess "build-in" means pointer type here. This sounds to me
as if we could modify the temporary object if it is of class type.
I'm quite confused....

Understandably, in this case.

The rules for the built-in operators involve something called
lvalue-ness: the resuls of an expression are either an lvalue,
or they are not. Certain built-in operators, like -- require an
lvalue. A function call is an lvalue if and only if the return
type is a reference. (As a first approximation, temporaries are
not lvalues, everything else is. But this is very approximate;
the standard uses a different language here for a reason.)

User overridden operators are functions, and obey the rules for
function calls, not the rules for the operator they are
overriding. Thus, if operator-- is a user defined override,
then whether it can be called depends on the rules for calling
functions. If it is a member function (usually the case), then
it can be called on a temporary, even if the function is
non-const (which it definitely should be). If it is a free
function, taking a non-const reference, it can't be.

So, if the iterator returned from v.end() is a pointer, or is a
class type with a free function operator--, the expression
"--v.end()" is illegal; if the iterator is a class type with a
member operator--, it is legal.
 
J

James Kanze

Depending on how the iterator's operator-- is written, it also might not
be legal. The subtlety here is that you can call non-const member
functions on a temporary object, but you cannot pass a temporary object
to a function through a non-const reference. The standard doesn't say
whether operator-- is a member function, just that --iter is valid and
has the appropriate effects.

Also, of course, the standard allows the use of a raw pointer as
iterator; in some implementations, vector<T>::iterator is just a
typedef for T*. In the case of a raw pointer, the rules for the
built-in operator -- apply, and not those for functions. And
the rules for the built-in operator -- require an lvalue, which
the return value of a function is not (unless the function
returns a reference).

(Just thought I'd add to the confusion.)
 
J

James Kanze

Victor Bazarov wrote:
A quick grep suggests that the answer is no, but I haven't been in that
code for a year or more.

That's the current version, right? I believe earlier versions
used a typedef to a pointer for the iterator in vector---that
was certainly the most prevalent implementation five years ago.

The result is that --v.end() might fail for someone using an
older version of the compiler (VC++ 6.0, g++ 2.95.2), but work
for someone using a more recent version (I haven't tried with
VC++ 8, but it doesn't work with g++ 3.0 up.)
 

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,983
Messages
2,570,187
Members
46,747
Latest member
jojoBizaroo

Latest Threads

Top