|
Boost : |
From: David Abrahams (david.abrahams_at_[hidden])
Date: 2002-03-07 18:59:38
----- Original Message -----
From: "rwgk" <rwgk_at_[hidden]>
> Take the example of std::vector<T> vec(). If vec goes out
> of scope, there is a loop with size() iterations from
> vec.begin() to vec.end() that calls delete for each element
> of the vector.
> If we suppress the construction of the elements, where
> could we get into trouble later (assuming that the user has
> explicitly requested uninitialized initialization and is
> therefore responsible for other side effects)?
> Isn't the answer: Only if T has a non-trivial destructor?
> What else could go wrong?
All kinds of things: you could assign to an uninitialized T or call one
of its member functions. Will this cause trouble? Only the author of T
knows for sure. Furthermore, T's very reason for being might be to take
action in its constructor. If that's the case, skipping the constructor
*in and of itself* might be trouble.
> Maximum generality:
>
> The new approach should work for any type, built-in or
> user-defined, given the current rules for the
> initialization of scalars, even if the designer has not
> thought about the initialization problem.
> Rationale:
> If the designer cares, he can make a conscious effort
> to meet the minimum requirements for instantiating a
> container of uninitialized values; if the designer
> does not care but the type lends itself "naturally" to
> uninitialized initialization, why shouldn't it just
> work?
>
> Full backward compatibility:
>
> Instantiation of a container with uninitialized values
> should be an additional constructor that does not interfere
> with the established std::vector or std::valarray
> interface.
>
> Least additional complexity:
>
> - The additional constructor should be intuitive.
> - It should be safe.
> - It should be easy to implement.
> - A portable implementation should be easy.
>
> A proposed solution that meets all the requirements:
>
> struct uninitialized_flag {};
>
> template <typename T>
> class vector
> {
> public:
> // ...
> vector(size_t n) // shown here to illustrate what happens under
> the hood
> {
> // allocator is called here
> std::uninitialized_fill_n(begin(), n, T());
> size_ = n;
> }
> vector(size_t n, uninitialized_flag)
> {
> // allocator is called here
> BOOST_STATIC_ASSERT(boost::has_trivial_destructor<T>::value);
> size_ = n;
> }
> // ...
> private:
> // ...
> size_t size_;
> // ...
> };
>
> The only extension of the current standard that is needed
> to allow for a portable implementation is that
> has_trivial_destructor<T> must be available.
Suppose you're writing a generic function which has to work for all
kinds of numeric T:
double, complex<float>, and array_with_shared_semantics<double> are
examples
You need to create a huge vector<T> at the beginning of the function,
and you want to initialize it piecemeal during the function's execution
by assigning values into it. You don't want to pay for initialization if
you don't have to. Note that you do have to initialize
array_with_shared_semantics<double>, because its assignment operator
inspects the current contents. How do you write this?
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk