Boost logo

Boost :

From: David Abrahams (david.abrahams_at_[hidden])
Date: 2002-03-08 12:55:07


----- Original Message -----
From: "Howard Hinnant" <hinnant_at_[hidden]>
To: <boost_at_[hidden]>
Sent: Friday, March 08, 2002 12:23 PM
Subject: Re: [boost] Re: container design (Was: std::complex design
(Was: N-Dimensional array))

> On Friday, March 8, 2002, at 11:16 AM, David Abrahams wrote:
>
> >> Sorry to be dense, but what vector invariant is broken?
> >
> > Oh, for example, that the vector can be copied or destroyed or the
0th
> > item can be dereferenced, depending on the definition of T and what
you
> > think uninitialized_flag actually does.
>
> I agree that we would not want to break any of these invariants.
>
> I feel that the uninitialized_flag option can be accomplished without
> breaking these invariants. Perhaps the misunderstanding is in the
> element:
>
> For vector<T> to be able to use the uninitialized_flag constructor, T
> must have a constructor that will execute in 0 instructions. If T has
> such a constructor, then who is to say whether or not vector looped
over
> all of the addresses and executed this constructor or not?
>
> Scalars have such a constructor. It is evident in code like:
>
> void foo()
> {
> double x; // x is now in scope and constructed. No code has
been
> generated.
> ...
> }

No, that's part of the point of Andy's paper. x is uninitialized, which
is just about as good (from a standards/portability POV) as not being
constructed. What can you do with x without invoking undefined behavior?
You can assign to it. That's it.

Why? Because for scalars, the assignment operator is a synonym for the
copy constructor.

> Here's another one:
>
> void foo2()
> {
> double* vec = new double[10000];
> // I now have an array of 10000 constructed doubles.
> // Though these doubles are constructed, their value is
indeterminant
> }
>
> I know that on some platforms some constructed but indeterminant
scalars

Since we're in the domain of numerics here, I get momentarily confused
every time you write "indeterminant" instead of "indeterminate". ;-)

> can cause problems if loaded into certain machine registers. But that
> does not seem to break vector invariants.

Yes, it breaks this function which is currently guaranteed to work:

double zeroth(std::vector<double> const& x)
{
    return x.empty() ? 0.0 : x[0];
}

In fact, I'm not even sure that x[0] is defined if the elements are
uninitialized.

> And on many machines (the one
> I'm typing on right now for example) even this is not a problem.

Right. Maybe it's just time to lift permission for handling
uninitialized scalars to cause undefined behavior.

> If you are on a platform where:
>
> v[1] = v[0];
>
> would cause a problem after a vector uninitialized constructor, then
> don't do that! It is no different from saying don't do this:
>
> double x, y;
> x = y;

Yes, but handling doubles that way is already unsafe, while handling
vectors is not.

> We don't need to mandate protection from such nonsense.

I don't like the idea of adding new forms of nonsense, especially those
which can occur at great remove from the original dangerous construct
and look innocuous (vectors are typically passed by-reference whereas
raw scalars are not).

> The protection
> is too expensive to mandate. However the protection is available (in
> vector) for those who want it:
>
> vector<double> v(10); // protected from uninitialized doubles
>
> Meanwhile vector<double> v(10, uninitialized_flag) breaks no vector
> invariants. The vector is destructible. The vector elements can be
> assigned into.

umm, v.insert(v.begin(), 1.0) is undefined.

> If your hardware doesn't mind (mine doesn't), you can
> even assign from this uninitialized vector. I can't think of a
reason
> why you would want to do that though.
>
> But even on hostile hardware such a vector can easily be made
assignable
> from and copyable by simply using memcpy instead of passing everything
> through a floating point register. And I simply can not imagine a
> platform where arbitrary bit patterns can not be moved via memcpy.
>
> And if telling vector to "use" the zero instruction constructor for T
is
> still a problem, then how is that problem solved by passing the vector
a
> chunk of memory that has been "initialized" with these /same/ zero
> instruction constructors? You've still got exactly the same vector!
> Except with an added problem: the allocator may not be compatible
with
> the way the memory was allocated outside of the vector.

I think you misunderstood the intent of that. The intent was that the
user does his dangerous uninitialized memory construction /outside/ of
vector in some raw memory, and all of the opportunities for mistakes
with vector are isolated at the point where the adoption occurs. This
would also allow the user to do fancy in-place construction of elements
with more-interesting constructors.

-Dave


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk