Topic: Must the elements of a container always be valid


Author: Valentin Bonnard <Valentin.Bonnard@free.fr>
Date: 2000/11/10
Raw View
James.Kanze@dresdner-bank.com wrote:

> In article <3A09926D.42A0@free.fr>,
>   Valentin Bonnard <Valentin.Bonnard@free.fr> wrote:
> > James.Kanze@dresdner-bank.com wrote:
>=20
> > > See the question in the subject line: must the elements of a
> > > standard container always remain valid?  I know that they must be
> > > valid (or at least copiable) in order to be inserted, but after
> > > that?
>=20
> > They must be valid whenever vector is allowed to copy its elements
> > (insert, erase, resize, reserve, but not begin, end, at).
>=20
> That is obvious.

Glad to see that you agree with me.

> > > The problem really occurs with pointers.  Is the following program
> > > valid, for example:
>=20
> > >     int
> > >     main()
> > >     {
> > >         std::vector< int* > a ;
> > >         for ( int i =3D 0 ; i < 3 ; ++ i ) {
> > >             a.push_back( new int( i ) ) ;
> > >         }
> > >         //  ...
> > >         for ( std::vector< int* >::iterator i =3D a.begin() ;
> > >                                             i !=3D a.end() ;
> > >                                              ++ i ) {
> > >             delete *i ;
>=20
> > Iterator over `a' cannot copy the elements, so the program is
> > correct.
>=20
> I don't really care about the iterator.  I'm concerned about the
> underlying container.  A safe iterator will deregister from the
> container, for example.  Could this deregister function read an
> element in the container?  Where does the standard forbid it?

What do you mean=A0? What is a read=A0?

In this context, I believe you mean lvalue-to-rvalue conversion.=20
But how can you force this conversion to happen=A0?

AFAIK, you can't *in general*=20
                     ^^^^^^^ (read generic).

For built-in types, you just cast the lvalue:

void lvalue_t_rvalue (builtin_type& x)
{
  (builtin_type) x;
}

This expression does a static_cast, and is equivalent=20
with =20

builtin_type annon =3D x;

Moral: in order to have a lvalue-to-rvalue conversion,=20
you need to do something else (in general). You can't=20
have just the conversion.

Has I have already said, begin, end, at functions of=20
containers are not allowed to copy objects.

>=20
> > >         }
> > >         return 0 ;
> > >     }
>=20
> Before the return, the destructor of the container is called.  May the
> destructor read one or more elements in the container?  If not, where
> does the standard forbid this?

The destructor isn't allowed to copy objects either.

--=20

Valentin Bonnard

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: James.Kanze@dresdner-bank.com
Date: 2000/11/07
Raw View
In article <3a072c5c.3267885@news.ntlworld.com>,
  the_wid@my-deja.com (Tom) wrote:
> On Mon,  6 Nov 2000 18:36:03 GMT, James.Kanze@dresdner-bank.com wrote:

> >See the question in the subject line: must the elements of a
> >standard container always remain valid?  I know that they must be
> >valid (or at least copiable) in order to be inserted, but after
> >that?

> What do you mean by "valid"?

In this case, capable of being accessed.

> >The problem really occurs with pointers.  Is the following program
> >valid, for example:

> >    int
> >    main()
> >    {
> >        std::vector< int* > a ;
> >        for ( int i = 0 ; i < 3 ; ++ i ) {
> >            a.push_back( new int( i ) ) ;
> >        }
> >        //  ...
> >        for ( std::vector< int* >::iterator i = a.begin() ;
> >                                            i != a.end() ;
> >                                             ++ i ) {
> >            delete *i ;
> >        }
> >        return 0 ;
> >    }

> Why should there be a problem with the pointers after deleting them?
> If you don't dereference them, no problem. They may point to invalid
> memory, but they have valid values (since new returned them).

A deleted pointer is invalid, and just accessing the pointer (and not
what it points to) invokes undefined behavior.

>From what I can gather from the standard, a container can read any
element it wants, at any time.  If this is true, the above code is
illegal, since if the container accesses one of the deleted pointers
in its destructor, undefined behavior occurs.

Of course, this makes is pretty hard to maintain a container of
pointers to dynamically allocated memory.  In this case, it may be
that additional restrictions on the implementation are in order.  I
can think of no reason why an implementation would actually access the
data value in its destructor, other than calling the destructor on it,
and I know of no implementation which does.  However, as long as this
is not guaranteed, I don't think that the above code is conform.

--
James Kanze                               mailto:kanze@gabi-soft.de
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
Ziegelh   ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627


Sent via Deja.com http://www.deja.com/
Before you buy.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: Ron Natalie <ron@sensor.com>
Date: 2000/11/07
Raw View

James.Kanze@dresdner-bank.com wrote:
>
> > >        for ( std::vector< int* >::iterator i = a.begin() ;
> > >                                            i != a.end() ;
> > >                                             ++ i ) {
> > >            delete *i ;
> > >        }
> > >        return 0 ;
> > >    }
>
> > Why should there be a problem with the pointers after deleting them?
> > If you don't dereference them, no problem. They may point to invalid
> > memory, but they have valid values (since new returned them).
>
> A deleted pointer is invalid, and just accessing the pointer (and not
> what it points to) invokes undefined behavior.
>
>
       for ( std::vector< int* >::iterator i = a.begin() ;
             i != a.end() ;
             ++ i ) {
            delete *i ;
     *i = 0;
        }

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: James.Kanze@dresdner-bank.com
Date: 2000/11/07
Raw View
In article <3A083082.63D90B61@sensor.com>,
  Ron Natalie <ron@sensor.com> wrote:

> James.Kanze@dresdner-bank.com wrote:

> > > >        for ( std::vector< int* >::iterator i = a.begin() ;
> > > >                                            i != a.end() ;
> > > >                                             ++ i ) {
> > > >            delete *i ;
> > > >        }
> > > >        return 0 ;
> > > >    }

> > > Why should there be a problem with the pointers after deleting
> > > them?  If you don't dereference them, no problem. They may point
> > > to invalid memory, but they have valid values (since new
> > > returned them).

> > A deleted pointer is invalid, and just accessing the pointer (and
> > not what it points to) invokes undefined behavior.

>        for ( std::vector< int* >::iterator i = a.begin() ;
>              i != a.end() ;
>              ++ i ) {
>             delete *i ;
>      *i = 0;
>         }

Even better, in the loop:

    T* p = NULL ;
    std::swap( *i , p ) ;
    delete p ;

I really can't imagine an implementation of vector (or any other
standard container) which read the existing element in the operator*
of the iterator, but I can't see where it would be non-conformant,
either.

Still, I'd really prefer it if the standard said that an
implementation couldn't read my pointers gratuitously.

--
James Kanze                               mailto:kanze@gabi-soft.de
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
Ziegelh   ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627


Sent via Deja.com http://www.deja.com/
Before you buy.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: brangdon@cix.compulink.co.uk
Date: 2000/11/07
Raw View
James.Kanze@dresdner-bank.com () wrote (abridged):
> [ Concerning
>         for ( std::vector< int* >::iterator i = a.begin() ;
>                                             i != a.end() ;
>                                              ++ i ) {
>             delete *i ;
>         }
> ... ]
>
> From what I can gather from the standard, a container can read any
> element it wants, at any time.  If this is true, the above code is
> illegal, since if the container accesses one of the deleted pointers
> in its destructor, undefined behavior occurs.

I agree. An obvious approach is to zap the container's pointer first.
Replace
    delete *i;

with:
    int *old = *i;
    *i = 0;
    delete old;


> Of course, this makes is pretty hard to maintain a container of
> pointers to dynamically allocated memory.

Pretty hard? The above is slightly tedious, but can be encapsulated in a
single function. In practice containers of pointers have to be careful
about exposing erase, resize etc anyway in case someone forgets to delete
the pointed-to objects altogether. Surely the extra work of writing 3
lines instead of 1, in a single function, is insignificant?


> I can think of no reason why an implementation would actually
> access the data value in its destructor, other than calling
> the destructor on it, and I know of no implementation which does.

One reason is to provide debugging aids. As in:

    void vector<T *>::assert_valid() const {
        for (const_iterator i = begin(); i != end(); ++i)
            assert( __IsValidAddress( *i ) );
    }

where __IsValidAddress() is some implementation-defined check.

  Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
      brangdon@cix.co.uk      |   And close your eyes with holy dread,
                              |  For he on honey dew hath fed
 http://www.bhresearch.co.uk/ |   And drunk the milk of Paradise."

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: the_wid@my-deja.com (Tom)
Date: Wed, 8 Nov 2000 00:51:00 GMT
Raw View
On Tue,  7 Nov 2000 14:54:41 GMT, James.Kanze@dresdner-bank.com wrote:

>A deleted pointer is invalid, and just accessing the pointer (and not
>what it points to) invokes undefined behavior.
>

I hadn't come across this before. Does this mean the following code is
undefined behaviour?

int main()
{
 int* i = new int(0);
 delete i;
 int* j = i;
}

I'm unsure of why that shouldn't work. And how about:

int main()
{
 int* i = new int(0);
 delete i;
 i = 0; //did I "access" the pointer there?
}


I find:

3.7.3.2p4
The effect of using an invalid
pointer value (including passing it to a deallocation function) is
undefined.33)
(
33) On some implementations, it causes a system-generated runtime
fault.
)
and

5.3.5 p4
[Note: the value of a pointer that refers to deallocated storage is
indetermi-nate.]

What does "using" mean? That doesn't mean that you can't access it, or
does it? Does it matter that it is "indeterminate"? Does that make the
code: float f; float f2 = f; ill-formed, since you are accessing an
indeterminate value?

Confused,

Tom

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: James.Kanze@dresdner-bank.com
Date: 2000/11/08
Raw View
In article <3a089558.523172@news.ntlworld.com>,
  the_wid@my-deja.com (Tom) wrote:
> On Tue,  7 Nov 2000 14:54:41 GMT, James.Kanze@dresdner-bank.com wrote:

> >A deleted pointer is invalid, and just accessing the pointer (and
> >not what it points to) invokes undefined behavior.

> I hadn't come across this before. Does this mean the following code
> is undefined behaviour?

> int main()
> {
>  int* i = new int(0);
>  delete i;
>  int* j = i;
> }

Yes.

> I'm unsure of why that shouldn't work.

Because there exist, or have existed, implementations where it would
have caused a trap or a hardware exception of some sort.

> And how about:

> int main()
> {
>  int* i = new int(0);
>  delete i;
>  i = 0; //did I "access" the pointer there?
> }

This should work.

> I find:

> 3.7.3.2p4
> The effect of using an invalid
> pointer value (including passing it to a deallocation function) is
> undefined.33)
> (
> 33) On some implementations, it causes a system-generated runtime
> fault.
> )
> and

> 5.3.5 p4
> [Note: the value of a pointer that refers to deallocated storage is
> indetermi-nate.]

> What does "using" mean? That doesn't mean that you can't access it,
> or does it?

It means that the *value* can't by used, anywhere.  Basically, I would
interpret this to mean that if a pointer contains an invalid value,
the lvalue to rvalue conversion -- that is, using the *value* -- is
what causes undefined behavior.

> Does it matter that it is "indeterminate"? Does that make the code:
> float f; float f2 = f; ill-formed, since you are accessing an
> indeterminate value?

Yes.  While modern implementations that will fault on the invalid
pointer are rare or inexistant, many current implementations will
fault on loading an invalid floating point value, which could
correspond to a signalling NaN, for example.

--
James Kanze                               mailto:kanze@gabi-soft.de
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
Ziegelh   ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627


Sent via Deja.com http://www.deja.com/
Before you buy.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: Valentin Bonnard <Valentin.Bonnard@free.fr>
Date: 2000/11/08
Raw View
James.Kanze@dresdner-bank.com wrote:

> See the question in the subject line: must the elements of a standard
> container always remain valid?  I know that they must be valid (or at
> least copiable) in order to be inserted, but after that?

They must be valid whenever vector is allowed to copy its elements
(insert, erase, resize, reserve, but not begin, end, at).

> The problem really occurs with pointers.  Is the following program
> valid, for example:
>
>     int
>     main()
>     {
>         std::vector< int* > a ;
>         for ( int i = 0 ; i < 3 ; ++ i ) {
>             a.push_back( new int( i ) ) ;
>         }
>         //  ...
>         for ( std::vector< int* >::iterator i = a.begin() ;
>                                             i != a.end() ;
>                                              ++ i ) {
>             delete *i ;

Iterator over `a' cannot copy the elements, so the program is
correct.

>         }
>         return 0 ;
>     }

--

Valentin Bonnard

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: Valentin Bonnard <Valentin.Bonnard@free.fr>
Date: 2000/11/08
Raw View
brangdon@cix.compulink.co.uk wrote:

> Surely the extra work of writing 3
> lines instead of 1, in a single function, is insignificant?

Writing 3 lines in order do something is insignificant, but
writing 3 lines instead of 1 for no reason is an unbearable
amount of work.

--

Valentin Bonnard

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: Francis Glassborow <francis.glassborow@ntlworld.com>
Date: 2000/11/08
Raw View
In article <3A099353.4E1D@free.fr>, Valentin Bonnard
<Valentin.Bonnard@free.fr> writes
>brangdon@cix.compulink.co.uk wrote:
>
>> Surely the extra work of writing 3
>> lines instead of 1, in a single function, is insignificant?
>
>Writing 3 lines in order do something is insignificant, but
>writing 3 lines instead of 1 for no reason is an unbearable
>amount of work.

Surely not, it triples my productivity :)


Francis Glassborow      Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: James.Kanze@dresdner-bank.com
Date: 2000/11/09
Raw View
In article <3A09926D.42A0@free.fr>,
  Valentin Bonnard <Valentin.Bonnard@free.fr> wrote:
> James.Kanze@dresdner-bank.com wrote:

> > See the question in the subject line: must the elements of a
> > standard container always remain valid?  I know that they must be
> > valid (or at least copiable) in order to be inserted, but after
> > that?

> They must be valid whenever vector is allowed to copy its elements
> (insert, erase, resize, reserve, but not begin, end, at).

That is obvious.

> > The problem really occurs with pointers.  Is the following program
> > valid, for example:

> >     int
> >     main()
> >     {
> >         std::vector< int* > a ;
> >         for ( int i = 0 ; i < 3 ; ++ i ) {
> >             a.push_back( new int( i ) ) ;
> >         }
> >         //  ...
> >         for ( std::vector< int* >::iterator i = a.begin() ;
> >                                             i != a.end() ;
> >                                              ++ i ) {
> >             delete *i ;

> Iterator over `a' cannot copy the elements, so the program is
> correct.

I don't really care about the iterator.  I'm concerned about the
underlying container.  A safe iterator will deregister from the
container, for example.  Could this deregister function read an
element in the container?  Where does the standard forbid it?

> >         }
> >         return 0 ;
> >     }

Before the return, the destructor of the container is called.  May the
destructor read one or more elements in the container?  If not, where
does the standard forbid this?

--
James Kanze                               mailto:kanze@gabi-soft.de
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
Ziegelh   ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627


Sent via Deja.com http://www.deja.com/
Before you buy.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: James.Kanze@dresdner-bank.com
Date: 2000/11/06
Raw View
See the question in the subject line: must the elements of a standard
container always remain valid?  I know that they must be valid (or at
least copiable) in order to be inserted, but after that?

The problem really occurs with pointers.  Is the following program
valid, for example:

    int
    main()
    {
        std::vector< int* > a ;
        for ( int i = 0 ; i < 3 ; ++ i ) {
            a.push_back( new int( i ) ) ;
        }
        //  ...
        for ( std::vector< int* >::iterator i = a.begin() ;
                                            i != a.end() ;
                                             ++ i ) {
            delete *i ;
        }
        return 0 ;
    }

And if not, how does one delete all of the pointers in  a container?

--
James Kanze                               mailto:kanze@gabi-soft.de
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
Ziegelh   ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627


Sent via Deja.com http://www.deja.com/
Before you buy.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: the_wid@my-deja.com (Tom)
Date: 2000/11/06
Raw View
On Mon,  6 Nov 2000 18:36:03 GMT, James.Kanze@dresdner-bank.com wrote:

>See the question in the subject line: must the elements of a standard
>container always remain valid?  I know that they must be valid (or at
>least copiable) in order to be inserted, but after that?

What do you mean by "valid"?

>
>The problem really occurs with pointers.  Is the following program
>valid, for example:
>
>    int
>    main()
>    {
>        std::vector< int* > a ;
>        for ( int i = 0 ; i < 3 ; ++ i ) {
>            a.push_back( new int( i ) ) ;
>        }
>        //  ...
>        for ( std::vector< int* >::iterator i = a.begin() ;
>                                            i != a.end() ;
>                                             ++ i ) {
>            delete *i ;
>        }
>        return 0 ;
>    }
>

Why should there be a problem with the pointers after deleting them?
If you don't dereference them, no problem. They may point to invalid
memory, but they have valid values (since new returned them).

Tom

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]