|
Boost : |
From: rwgk (rwgk_at_[hidden])
Date: 2002-03-07 18:10:21
--- In boost_at_y..., "David Abrahams" <david.abrahams_at_r...> wrote:
> Yes, that's the definition of trivial destructor, but it's just not
the
> right criterion for deciding whether members must be initialized in
the
> constructor. For example, one member might be a type with a trivial
> destructor but whose constructor does something important that
can't be
> skipped.
Prerequisite:
The current rules for the initialization of scalars are
god-given. There are ample ways for someone writing a
class to provide a constructor that does nothing.
Paradigm:
In the context of initializing elements of a container I am
trying to follow the path of least additional complexity,
minimum restrictions, maximum generality and full backward
compatibility.
Minimum restrictions:
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?
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.
Example implementation:
http://cci.lbl.gov/~rwgk/shortcuts/cctbx/include/cctbx/array_family/
(also available at cctbx.sf.net, currently branch
array_family_integration)
Most relevant files:
small_plain.h
shared_plain.h
versa_plain.h
Look for "no_initialization_flag".
These types are used (i.e. tested) in a major application.
Note that the array family does not include a direct competition
for std::vector or std::valarray. Therefore the extension proposed
above is still relevant (to me, anyway).
Portability (with a few simple hacks related to
has_trivial_destructor):
VC6 (well, this is cheating, the safety check is simply turned off)
Codewarrior 7, Win32
gcc 3.0.x
EDG 238
EDG 240
EDG 243
EDG 245
Ralf
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk