Topic: Empty vector with legacy interface


Author: deepblack@geocities.com (Luis Coelho)
Date: 1999/09/23
Raw View
On 17 Sep 99 20:36:27 GMT, Lisa Lippincott
<lisa_lippincott@bigfix.com> uttered the following words:

>I'm beginning to think that to take much advantage of the contiguous-vector
>change, we also conversions from vector<T>::iterator to T* and
>vector<T>::const_iterator to const T*.
>
>I can't see any problems with mandating these conversions.  Can anyone
>else?

I might be wrong, but I seem to recall having seen someone in
this ng (or in comp.lang.c++.mod) post an argument that
vector<T>::iterator must be T*. It was an argument based on how
vector<T>::iterator must be allocator<T>::pointer and
allocator<T>::pointer must be T*.
Since I don't own a copy of the standard I cannot check if it is
true, could someone enlighten me please.

regards,
Luis Coelho.
C++ Programming Language, 3rd Ed. by B. Stroustrup. My exercise answers at:
http://www.geocities.com/SiliconValley/Way/3972/index.html


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Ed Brey" <brey@afd.mke.etn.com>
Date: 1999/09/20
Raw View
Siemel B. Naran <sbnaran@uiuc.edu> wrote in message
news:slrn7u6boi.a4f.sbnaran@localhost.localdomain...
> On 17 Sep 99 20:36:27 GMT, Lisa Lippincott <lisa_lippincott@bigfix.com>
wrote:
>
> >I'm beginning to think that to take much advantage of the
contiguous-vector
> >change, we also conversions from vector<T>::iterator to T* and
> >vector<T>::const_iterator to const T*.
> >
> >I can't see any problems with mandating these conversions.  Can anyone
> >else?
>
> You mean implicit conversions?
>
> Implicit conversions are bad because they may result in the wrong
> function being called.  Many of these errors we'll find at compile
> time, but the error messages will be annoying.  Some errors will
> only be discovered at runtime, and they will be very hard to
> track down.  And finally, people reading your code may be confused
> because they'll have to think twice when they see "f(v)" -- does
> this call f(std::vector<int>) or f(int *).  It's annoying for
> them to have to browse back and forth all the time.

I agree with you that conversions from vector<T> to T* are problamatic.
However, the conversions that I have been contemplating, and that Lisa
mentioned, are from vector<T>::iterator to T*.

Given your examples, f(v) would bind without question to f(vector<int>).  To
call f(int*), you would say f(v.begin()).  The only confusion would occure
if you created two functions f(vector<int>::iterator) and f(int*).  However,
overloads such as these are rare and probably deserve confustion anyway.

Given the usage patters, I don't see any problem with mandating that a
iterator be convertable to a pointer.

The next question is whether the conversion should be implicit.  Since
overloads that take an iterator and pointer are rare, I don't see any
problem in this either.

An implicit conversion would eliminate the need for a data memeber, because
begin would be equivalent.

I think about the worst case scenario is the current one, where the
conversion between an iterator and a pointer is undefined.  It should be
allowed or disallowed, requiring a diagnostic.  Currently, one can write a
program that makes use of an implicit conversion, and it compiles are runs
fine.  Since the behavious is undefined by the standard, however, the code
may fail when ported.  Since relying on automatic conversion is such common
place for a bug, it would really be nice if the compiler was forced to flag
it.  A QOI issue?  Maybe.  But as it stands, I expect that customers may
begin defining a quality compiler as one whose implementation happens to
include implicit conversion, so as not to break nievely buggy code.

> Also, because class std::vector is to be an example of how to write
> good code, it should not have operator conversions at all.

Agreed, because a vector conceptually very different from a pointer.  For
iterator, which is conceptually very similiar to a pointer, an implicit
conversion would be good code.

>
> Explicit conversions, like a member function data(), are bad
> because they sanction thinking of the data as a vector<T> as a
> T *.  So a non-member function data() may be better.

This is a good argument for using a begin() instead of data() to get a
pointer to the contents.  begin() makes it clear that vector<T> should be
thought of as a T[] (which is fine) and returns a pointer that happens to
point to the first element.




[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Lisa Lippincott <lisa_lippincott@bigfix.com>
Date: 1999/09/17
Raw View
I'm beginning to think that to take much advantage of the contiguous-vector
change, we also conversions from vector<T>::iterator to T* and
vector<T>::const_iterator to const T*.

I can't see any problems with mandating these conversions.  Can anyone
else?

                                                   --Lisa Lippincott
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: sbnaran@uiuc.edu (Siemel B. Naran)
Date: 1999/09/18
Raw View
On 17 Sep 99 20:36:27 GMT, Lisa Lippincott <lisa_lippincott@bigfix.com> wrote:

>I'm beginning to think that to take much advantage of the contiguous-vector
>change, we also conversions from vector<T>::iterator to T* and
>vector<T>::const_iterator to const T*.
>
>I can't see any problems with mandating these conversions.  Can anyone
>else?

You mean implicit conversions?

Implicit conversions are bad because they may result in the wrong
function being called.  Many of these errors we'll find at compile
time, but the error messages will be annoying.  Some errors will
only be discovered at runtime, and they will be very hard to
track down.  And finally, people reading your code may be confused
because they'll have to think twice when they see "f(v)" -- does
this call f(std::vector<int>) or f(int *).  It's annoying for
them to have to browse back and forth all the time.

Also, because class std::vector is to be an example of how to write
good code, it should not have operator conversions at all.

Explicit conversions, like a member function data(), are bad
because they sanction thinking of the data as a vector<T> as a
T *.  So a non-member function data() may be better.

--
--------------
siemel b naran
--------------
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/09/17
Raw View
Ed Brey wrote:

> Assuming that the DR requiring vector be stored like an array goes through,
> I still see a potential problem using vector with legacy code if the vector
> happens to be empty.

I know, I know.

> Assume a legacy interface:
> void fn(int array[], size_t size);  // If size is 0, array is ignored.
>
> My calling code might look like:
>
> std::vector<int> v;
> // ...
> fn(v.begin(), v.size());
>
> However, AFAICT from the standard, the above won't work, since v.begin()
> returns an iterator, and there isn't necessarily a conversion from an
> iterator to a pointer.

You are correct.

> One way around this is to use:
> fn(&*v.begin(), v.size());
>
> However, this is undefined when the vector is empty.

Of course it is.

> Another option is to
> resort to:
>
> fn(v.size() ? &*v.begin() : 0, v.size());

I have already written two helpers functions for this purpose:

#include <vector>

// A::pointer shall be T*
// non precondition: v is _not_ required to have any elements
template <typename T, class A>
inline
T* data (std::vector<T,A>& v) // as string::data (), but non const
{
  return !v.empty() ? &v.front() : NULL;
}

// A::pointer shall be T*
// non precondition: v is _not_ required to have any elements
template <typename T, class A>
inline
const T* data (const std::vector<T,A>& v) // as string::data ()
{
  return !v.empty() ? &v.front() : NULL;
}

So for sring I write:

  fn (s.data(), s.size());

for vector it's:

  fn (data (v), v.size());

If I have:

  fn2 (int* begin, int* end);

I write:

  fn2 (data (v), data (v) + v.size ());

(Actually, I have never ever used the above two
helper functions, but I would use them if I had
to use some old-style interface.)

--

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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: sbnaran@uiuc.edu (Siemel B. Naran)
Date: 1999/09/17
Raw View
On 16 Sep 99 04:26:34 GMT, Ed Brey <brey@afd.mke.etn.com> wrote:

>Assume a legacy interface:
>void fn(int array[], size_t size);  // If size is 0, array is ignored.
>
>My calling code might look like:
>
>std::vector<int> v;
>// ...
>fn(v.begin(), v.size());

Most implementations have
   std::vector<T>::iterator       == T       *
   std::vector<T>::const_iterator == T const *
and so you don't have a problem.

An implementation may turn iterator and const_iterator into classes.
One reason to do this is to endow the iterator with typedefs, most
notably 'value_type'.  Another reason is to prevent the implicit
conversion of iterator to const_iterator.  Are there any other
reasons?


>However, AFAICT from the standard, the above won't work, since v.begin()
>returns an iterator, and there isn't necessarily a conversion from an
>iterator to a pointer.

Right.


>One way around this is to use:
>fn(&*v.begin(), v.size());
>
>However, this is undefined when the vector is empty.  Another option is to
>resort to:

Right.  Doing *v.begin() on an empty vector may throw an exception or
assert.  Indeed, &* is not equal to the identity operator.

This is also undefined if the vector is empty
   fn(&v[0],v.size());


>fn(v.size() ? &*v.begin() : 0, v.size());
>
>But this is ugly and inefficient.  Is there a better way?

Right.


I can't think of any way.
I did look to see if there is a function vector<T>::data() const, but
there doesn't seem to be one.  Maybe they should add this function in
the defect report?

--
--------------
siemel b naran
--------------
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: scott douglass <sdouglass%_%junk@_.arm.com>
Date: 1999/09/17
Raw View
Valentin Bonnard wrote:
> // A::pointer shall be T*
> // non precondition: v is _not_ required to have any elements
> template <typename T, class A>
> inline
> const T* data (const std::vector<T,A>& v) // as string::data ()
> {
>   return !v.empty() ? &v.front() : NULL;
> }
>
> [...]
>
> If I have:
>
>   fn2 (int* begin, int* end);
>
> I write:
>
>   fn2 (data (v), data (v) + v.size ());

I don't think 'data (v) + v.size ()' is legal if the vector is empty.  I think
'((char *)0) + 0' is undefined.
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Ed Brey" <brey@afd.mke.etn.com>
Date: 1999/09/16
Raw View
Assuming that the DR requiring vector be stored like an array goes through,
I still see a potential problem using vector with legacy code if the vector
happens to be empty.

Assume a legacy interface:
void fn(int array[], size_t size);  // If size is 0, array is ignored.

My calling code might look like:

std::vector<int> v;
// ...
fn(v.begin(), v.size());

However, AFAICT from the standard, the above won't work, since v.begin()
returns an iterator, and there isn't necessarily a conversion from an
iterator to a pointer.

One way around this is to use:
fn(&*v.begin(), v.size());

However, this is undefined when the vector is empty.  Another option is to
resort to:

fn(v.size() ? &*v.begin() : 0, v.size());

But this is ugly and inefficient.  Is there a better way?

If the DR which has the intent of making life easier for legacy users goes
through, I hope that the committee will make sure that life is easy no
matter what the size of the vector.

FWIW, "legacy" systems will be around for quite a while, since I'm
constantly writing new legacy interfaces like fn even now, since I'm forced
to use M$'s COM.
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]