mirror of
https://github.com/boostorg/utility.git
synced 2025-05-09 15:04:00 +00:00
585 lines
20 KiB
Plaintext
585 lines
20 KiB
Plaintext
[/
|
|
/ Copyright (c) 2012 Marshall Clow
|
|
/ Copyright (c) 2021, Alan Freitas
|
|
/
|
|
/ Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
/ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
/]
|
|
|
|
[/===============]
|
|
[#sec:value_init]
|
|
[section Value Init]
|
|
|
|
[/===============]
|
|
|
|
[section Introduction]
|
|
|
|
Constructing and initializing objects in a generic way is difficult in
|
|
C++. The problem is that there are several different rules that apply
|
|
for initialization. Depending on the type, the value of a newly constructed
|
|
object can be zero-initialized (logically 0), default-constructed (using
|
|
the default constructor), or indeterminate. When writing generic code,
|
|
this problem must be addressed. The template __value_initialized__ provides
|
|
a solution with consistent syntax for value initialization of scalar,
|
|
union and class types. Moreover, __value_initialized__ offers a workaround to various
|
|
compiler issues regarding value-initialization.
|
|
|
|
Furthermore, a `const` object __initialized_value__ is provided,
|
|
to avoid repeating the type name when retrieving the value from a
|
|
`__value_initialized__<T>` object.
|
|
|
|
There are various ways to initialize a variable, in C++. The following
|
|
declarations all ['may] have a local variable initialized to its default
|
|
value:
|
|
|
|
```
|
|
T1 var1;
|
|
T2 var2 = 0;
|
|
T3 var3 = {};
|
|
T4 var4 = T4();
|
|
```
|
|
|
|
Unfortunately, whether or not any of those declarations correctly
|
|
initialize the variable very much depends on its type. The first
|
|
declaration is valid for any __DefaultConstructible__ type by definition.
|
|
|
|
However, it does not always do an initialization. It correctly initializes
|
|
the variable when it's an instance of a class, and the author of the class
|
|
has provided a proper default constructor. On the other hand, the value of
|
|
`var1` is ['indeterminate] when its type is an arithmetic type, like `int`,
|
|
`float`, or `char`.
|
|
|
|
An arithmetic variable is of course initialized properly by the second declaration,
|
|
`T2 var2 = 0`. But this initialization form will not usually work for a
|
|
class type, unless the class was especially written to support being
|
|
initialized that way.
|
|
|
|
The third form, `T3 var3 = {}`, initializes an aggregate, typically a "C-style"
|
|
`struct` or a "C-style" array. However, at the time this library was developed,
|
|
the syntax did not allow for a class that has an explicitly declared constructor.
|
|
|
|
The fourth form is the most generic form of them, as it can be used to initialize
|
|
arithmetic types, class types, aggregates, pointers, and other types.
|
|
The declaration, `T4 var4 = T4()`, should be read as follows: First a temporary
|
|
object is created, by `T4()`. This object is [link sec:valueinit value-initialized].
|
|
Next the temporary object is copied to the named variable, `var4`. Afterwards,
|
|
the temporary is destroyed. While the copying and the destruction are likely to
|
|
be optimized away, C++ still requires the type `T4` to be __CopyConstructible__.
|
|
So `T4` needs to be ['both] __DefaultConstructible__ ['and] __CopyConstructible__.
|
|
|
|
A class may not be CopyConstructible, for example because it may have a
|
|
private and undefined copy constructor, or because it may be derived from
|
|
`boost::noncopyable`. Scott Meyers \[[link sec:references 2]\] explains why a
|
|
class would be defined like that.
|
|
|
|
There is another, less obvious disadvantage to the fourth form, `T4 var4 = T4()`:
|
|
It suffers from various [link sec:compiler_issues compiler issues], causing
|
|
a variable to be left uninitialized in some compiler specific cases.
|
|
|
|
The template __value_initialized__ offers a generic way to initialize
|
|
an object, like `T4 var4 = T4()`, but without requiring its type
|
|
to be __CopyConstructible__. And it offers a workaround to those compiler issues
|
|
regarding value-initialization as well. It allows getting an initialized
|
|
variable of any type; it ['only] requires the type to be __DefaultConstructible__.
|
|
A properly ['value-initialized] object of type `T` is constructed by the following
|
|
declaration:
|
|
|
|
```
|
|
value_initialized<T> var;
|
|
```
|
|
|
|
The template __initialized__ offers both value-initialization and direct-initialization.
|
|
It is especially useful as a data member type, allowing the very same object
|
|
to be either direct-initialized or value-initialized.
|
|
|
|
The `const` object __initialized_value__ allows value-initializing a variable as follows:
|
|
|
|
```
|
|
T var = initialized_value;
|
|
```
|
|
|
|
This form of initialization is semantically equivalent to `T4 var4 = T4()`,
|
|
but robust against the aforementioned compiler issues.
|
|
|
|
[endsect]
|
|
|
|
[#sec:details]
|
|
[section Details]
|
|
|
|
The C++ standard \[[link sec:references 3]\] contains the definitions
|
|
of `zero-initialization` and `default-initialization`. Informally, zero-initialization
|
|
means that the object is given the initial value `0` converted to the type and
|
|
default-initialization means that [@https://en.cppreference.com/w/cpp/named_req/PODType POD] \[[link sec:references 4]\] types are zero-initialized,
|
|
while non-POD class types are initialized with their corresponding default constructors.
|
|
|
|
A ['declaration] can contain an ['initializer], which specifies the
|
|
object's initial value. The initializer can be just '()', which states that
|
|
the object shall be value-initialized (but see below). However, if a ['declaration]
|
|
has no ['initializer] and it is of a non-`const`, non-`static` POD type, the
|
|
initial value is indeterminate: (see [sect]8.5, \[dcl.init\], for the
|
|
accurate definitions).
|
|
|
|
```
|
|
int x; // no initializer. x value is indeterminate.
|
|
__std_string__ s; // no initializer, s is default-constructed.
|
|
|
|
int y = int();
|
|
// y is initialized using copy-initialization
|
|
// but the temporary uses an empty set of parentheses as the initializer,
|
|
// so it is default-constructed.
|
|
// A default constructed POD type is zero-initialized,
|
|
// therefore, y == 0.
|
|
|
|
void foo ( __std_string__ ) ;
|
|
foo ( __std_string__() ) ;
|
|
// the temporary string is default constructed
|
|
// as indicated by the initializer ()
|
|
```
|
|
|
|
[#sec:valueinit]
|
|
[h5 value-initialization]
|
|
|
|
The first [@http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html Technical
|
|
Corrigendum for the C++ Standard] (TC1), whose draft was released to the public in
|
|
November 2001, introduced [@http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#178 Core
|
|
Issue 178], among many other issues.
|
|
|
|
That issue introduced the new concept of `value-initialization`, and also fixed the
|
|
wording for zero-initialization. Informally, value-initialization is similar to
|
|
default-initialization with the exception that in some cases non-static data members
|
|
and base class sub-objects are also value-initialized.
|
|
|
|
The difference is that an object that is value-initialized will not have, or at least
|
|
is less likely to have, indeterminate values for data members and base class sub-objects;
|
|
unlike the case of an object default constructed (see Core Issue 178 for a
|
|
normative description).
|
|
|
|
In order to specify value-initialization of an object we need to use the
|
|
empty-set initializer: `()`.
|
|
|
|
As before, a declaration with no initializer specifies default-initialization,
|
|
and a declaration with a non-empty initializer specifies copy (`=xxx`) or
|
|
direct (`xxx`) initialization.
|
|
|
|
```
|
|
template<class T> void eat(T);
|
|
|
|
int x ; // indeterminate initial value.
|
|
|
|
__std_string__ s; // default-initialized.
|
|
|
|
eat ( int() ) ; // value-initialized
|
|
|
|
eat ( __std_string__() ) ; // value-initialized
|
|
```
|
|
|
|
[#sec:valueinitsyn]
|
|
[h5 value-initialization syntax]
|
|
|
|
Value initialization is specified using `()`. However, the empty set of
|
|
parentheses is not permitted by the syntax of initializers because it is
|
|
parsed as the declaration of a function taking no arguments:
|
|
|
|
```
|
|
int x() ; // declares function int(*)()
|
|
```
|
|
|
|
Thus, the empty `()` must be put in some other initialization context.
|
|
|
|
One alternative is to use copy-initialization syntax:
|
|
|
|
```
|
|
int x = int();
|
|
```
|
|
|
|
This works perfectly fine for POD types. But for non-POD class types,
|
|
copy-initialization searches for a suitable constructor, which could be,
|
|
for instance, the copy-constructor. It also searches for a suitable conversion
|
|
sequence but this does not apply in this context.
|
|
|
|
For an arbitrary unknown type, using this syntax may not have the
|
|
value-initialization effect intended because we don't know if a copy
|
|
from a default constructed object is exactly the same as a default
|
|
constructed object, and the compiler is allowed, in
|
|
some cases, but never required to, optimize the copy away.
|
|
|
|
One possible generic solution is to use value-initialization of a non static
|
|
data member:
|
|
|
|
```
|
|
template<class T>
|
|
struct W
|
|
{
|
|
// value-initialization of 'data' here.
|
|
W() : data() {}
|
|
|
|
T data;
|
|
};
|
|
|
|
W<int> w;
|
|
// w.data is value-initialized for any type.
|
|
```
|
|
|
|
This is the solution as it was supplied by earlier versions of the
|
|
`__value_initialized__<T>` template class. Unfortunately this approach
|
|
suffered from various compiler issues.
|
|
|
|
[#sec:compiler_issues]
|
|
[h5 Compiler issues]
|
|
|
|
Various compilers have not yet fully implemented value-initialization.
|
|
So when an object should be ['value-initialized] according to the C++ Standard,
|
|
it ['may] in practice still be left uninitialized, because of those
|
|
compiler issues. It is hard to make a general statement on what those issues
|
|
are like, because they depend on the compiler you are using, its version number,
|
|
and the type of object you would like to have value-initialized.
|
|
|
|
All compilers we have tested so far support value-initialization for arithmetic types properly.
|
|
However, various compilers may leave some types of ['aggregates] uninitialized, when they
|
|
should be value-initialized. Value-initialization of objects of a pointer-to-member type may also
|
|
go wrong on various compilers.
|
|
|
|
At the moment of writing, May 2010, the following reported issues regarding
|
|
value-initialization are still there in current compiler releases:
|
|
|
|
|
|
|
|
* [@https://connect.microsoft.com/VisualStudio/feedback/details/100744 Microsoft Visual Studio Feedback ID 100744, Value-initialization in new-expression]: Reported by Pavel Kuznetsov (MetaCommunications Engineering), 2005.
|
|
* [@http://connect.microsoft.com/VisualStudio/feedback/details/484295 Microsoft Visual Studio Feedback ID 484295, VC++ does not value-initialize members of derived classes without user-declared constructor] Reported by Sylvester Hesp, 2009.
|
|
* [@https://connect.microsoft.com/VisualStudio/feedback/details/499606 Microsoft Visual Studio Feedback ID 499606, Presence of copy constructor breaks member class initialization] Reported by Alex Vakulenko, 2009
|
|
* [@http://qc.embarcadero.com/wc/qcmain.aspx?d=83751 Embarcadero/C++Builder Report 83751, Value-initialization: arrays should have each element value-initialized] Reported by Niels Dekker (LKEB), 2010.
|
|
* [@http://qc.embarcadero.com/wc/qcmain.aspx?d=83851 Embarcadero/C++Builder Report 83851, Value-initialized temporary triggers internal backend error C1798] Reported by Niels Dekker, 2010.
|
|
* [@http://qc.embarcadero.com/wc/qcmain.aspx?d=84279 Embarcadero/C++Builder Report 84279, Internal compiler error (F1004), value-initializing member function pointer by "new T()"] Reported by Niels Dekker, 2010
|
|
* Sun CR 6947016, Sun 5.10 may fail to value-initialize an object of a non-POD aggregate. Reported to Steve Clamage by Niels Dekker, 2010.
|
|
* IBM's XL V10.1 and V11.1 may fail to value-initialize a temporary of a non-POD aggregate. Reported to Michael Wong by Niels Dekker, 2010.
|
|
* Intel support issue 589832, Attempt to value-initialize pointer-to-member triggers internal error on Intel 11.1. Reported by John Maddock, 2010.
|
|
|
|
Note that all known GCC issues regarding value-initialization are fixed with GCC version 4.4, including
|
|
[@http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30111 GCC Bug 30111]. Clang also has completely implemented
|
|
value-initialization, as far as we know, now that [@http://llvm.org/bugs/show_bug.cgi?id=7139 Clang Bug 7139]
|
|
is fixed.
|
|
|
|
New versions of __value_initialized__ (Boost release version 1.35 or higher) offer a workaround to these
|
|
issues: __value_initialized__ may now clear its internal data, prior to constructing the object that it
|
|
contains. It will do so for those compilers that need to have such a workaround, based on the
|
|
[@boost:/doc/html/config/doc/html/boost_config/boost_macro_reference.html#boost_config.boost_macro_reference.macros_that_describe_defects
|
|
compiler defect macro] `BOOST_NO_COMPLETE_VALUE_INITIALIZATION`.
|
|
|
|
[endsect]
|
|
|
|
[#sec:types]
|
|
[section Types and objects]
|
|
|
|
[#sec:val_init]
|
|
[section `template class value_initialized<T>`]
|
|
|
|
```
|
|
namespace boost {
|
|
|
|
template<class T>
|
|
class __value_initialized__
|
|
{
|
|
|
|
public :
|
|
|
|
__value_initialized__() : x() {}
|
|
|
|
operator T const &() const { return x ; }
|
|
|
|
operator T&() { return x ; }
|
|
|
|
T const &data() const { return x ; }
|
|
|
|
T& data() { return x ; }
|
|
|
|
void swap( __value_initialized__& );
|
|
|
|
private :
|
|
|
|
[unspecified] x ;
|
|
|
|
} ;
|
|
|
|
template<class T>
|
|
|
|
T const& get ( __value_initialized__<T> const& x )
|
|
{
|
|
return x.data();
|
|
}
|
|
|
|
template<class T>
|
|
T& get ( __value_initialized__<T>& x )
|
|
{
|
|
return x.data();
|
|
}
|
|
|
|
template<class T>
|
|
void swap ( __value_initialized__<T>& lhs, __value_initialized__<T>& rhs )
|
|
{
|
|
lhs.swap(rhs);
|
|
}
|
|
|
|
} // namespace boost
|
|
```
|
|
|
|
An object of this template class is a `T`-wrapper convertible to `'T&'` whose
|
|
wrapped object (data member of type `T`) is [link sec:valueinit value-initialized] upon default-initialization
|
|
of this wrapper class:
|
|
|
|
```
|
|
int zero = 0;
|
|
__value_initialized__<int> x;
|
|
assert( x == zero ) ;
|
|
|
|
__std_string__ def;
|
|
__value_initialized__< __std_string__ > y;
|
|
assert( y == def ) ;
|
|
```
|
|
|
|
The purpose of this wrapper is to provide a consistent syntax for value initialization
|
|
of scalar, union and class types (POD and non-POD) since the correct syntax for value
|
|
initialization varies (see [link sec:valueinitsyn value-initialization syntax]).
|
|
|
|
The wrapped object can be accessed either through the conversion operator
|
|
`T&`, the member function `data()`, or the non-member function `get()`:
|
|
|
|
```
|
|
void watch(int);
|
|
|
|
__value_initialized__<int> x;
|
|
|
|
watch(x) ; // operator T& used.
|
|
watch(x.data());
|
|
watch( get(x) ) // function get() used
|
|
```
|
|
|
|
Both `const` and non-`const` objects can be wrapped. Mutable objects can be
|
|
modified directly from within the wrapper but constant objects cannot:
|
|
|
|
When `T` is a __Swappable__ type, `__value_initialized__<T>`
|
|
is swappable as well, by calling its `swap` member function
|
|
as well as by calling `boost::swap`.
|
|
|
|
```
|
|
__value_initialized__<int> x;
|
|
static_cast<int&>(x) = 1 ; // OK
|
|
get(x) = 1 ; // OK
|
|
|
|
__value_initialized__<int const> y ;
|
|
static_cast<int&>(y) = 1 ; // ERROR: cannot cast to int&
|
|
static_cast<int const&>(y) = 1 ; // ERROR: cannot modify a const value
|
|
get(y) = 1 ; // ERROR: cannot modify a const value
|
|
```
|
|
|
|
[warning
|
|
|
|
The __value_initialized__ implementation of Boost version 1.40.0 and older
|
|
allowed ['non-const] access to the wrapped object, from a constant wrapper,
|
|
both by its conversion operator and its `data()` member function.
|
|
|
|
For example:
|
|
|
|
```
|
|
__value_initialized__<int> const x_c;
|
|
int& xr = x_c ; // OK, conversion to int& available even though x_c is itself const.
|
|
xr = 2 ;
|
|
```
|
|
|
|
The reason for this obscure behavior was that some compilers did not accept the following valid code:
|
|
|
|
```
|
|
struct X
|
|
{
|
|
operator int&() ;
|
|
operator int const&() const ;
|
|
};
|
|
X x ;
|
|
(x == 1) ; // ERROR HERE!
|
|
```
|
|
|
|
The current version of __value_initialized__ no longer has this obscure behavior.
|
|
As compilers nowadays widely support overloading the conversion operator by having a `const`
|
|
and a `non-const` version, we have decided to fix the issue accordingly. So the current version
|
|
supports the idea of logical constness.
|
|
|
|
]
|
|
|
|
[h5 Recommended practice: The non-member get() idiom]
|
|
|
|
The obscure behavior of being able to modify a non-`const`
|
|
wrapped object from within a constant wrapper (as was supported by previous
|
|
versions of __value_initialized__) can be avoided if access to the wrapped object
|
|
is always performed with the `get()` idiom:
|
|
|
|
```
|
|
value_initialized<int> x;
|
|
get(x) = 1; // OK
|
|
value_initialized<int const> cx;
|
|
get(x) = 1; // ERROR: Cannot modify a const object
|
|
|
|
value_initialized<int> const x_c;
|
|
get(x_c) = 1; // ERROR: Cannot modify a const object
|
|
|
|
value_initialized<int const> const cx_c;
|
|
get(cx_c) = 1; // ERROR: Cannot modify a const object
|
|
```
|
|
|
|
[endsect]
|
|
|
|
[#sec:initialized]
|
|
[section `template class initialized<T>`]
|
|
|
|
|
|
```
|
|
namespace boost {
|
|
|
|
template<class T>
|
|
class __initialized__
|
|
{
|
|
|
|
public :
|
|
|
|
__initialized__() : x() {}
|
|
|
|
explicit __initialized__(T const & arg) : x(arg) {}
|
|
|
|
operator T const &() const;
|
|
|
|
operator T&();
|
|
|
|
T const &data() const;
|
|
|
|
T& data();
|
|
|
|
void swap( __initialized__& );
|
|
|
|
private :
|
|
|
|
[unspecified] x ;
|
|
|
|
};
|
|
|
|
template<class T>
|
|
T const& get ( __initialized__<T> const& x );
|
|
|
|
template<class T>
|
|
T& get ( __initialized__<T>& x );
|
|
|
|
template<class T>
|
|
void swap ( __initialized__<T>& lhs, __initialized__<T>& rhs );
|
|
|
|
} // namespace boost
|
|
```
|
|
|
|
The template class `boost::__initialized__<T>` supports both value-initialization
|
|
and direct-initialization, so its interface is a superset of the interface
|
|
of `__value_initialized__<T>`: Its default-constructor value-initializes the
|
|
wrapped object just like the default-constructor of `__value_initialized__<T>`,
|
|
but `boost::__initialized__<T>` also offers an extra `explicit`
|
|
constructor, which direct-initializes the wrapped object by the specified value.
|
|
|
|
`__initialized__<T>` is especially useful when the wrapped
|
|
object must be either value-initialized or direct-initialized, depending on
|
|
runtime conditions. For example, `__initialized__<T>` could
|
|
hold the value of a data member that may be value-initialized by some
|
|
constructors, and direct-initialized by others.
|
|
|
|
On the other hand, if it is known beforehand that the
|
|
object must ['always] be value-initialized, `__value_initialized__<T>`
|
|
may be preferable. And if the object must always be
|
|
direct-initialized, none of the two wrappers really needs to be used.
|
|
|
|
[endsect]
|
|
|
|
[#sec:initialized_value]
|
|
[section `initialized_value`]
|
|
|
|
```
|
|
namespace boost {
|
|
class __initialized_value_t__
|
|
{
|
|
public :
|
|
template <class T> operator T() const ;
|
|
};
|
|
|
|
__initialized_value_t__ const initialized_value = {} ;
|
|
|
|
} // namespace boost
|
|
```
|
|
|
|
__initialized_value__ provides a convenient way to get
|
|
an initialized value: its conversion operator provides an appropriate
|
|
['value-initialized] object for any __CopyConstructible__ type.
|
|
|
|
Suppose you need to have an initialized variable of type `T`.
|
|
You could do it as follows:
|
|
|
|
```
|
|
T var = T();
|
|
```
|
|
|
|
But as mentioned before, this form suffers from various compiler issues.
|
|
The template __value_initialized__ offers a workaround:
|
|
|
|
```
|
|
T var = get( __value_initialized__<T>() );
|
|
```
|
|
|
|
Unfortunately both forms repeat the type name, which
|
|
is rather short now (`T`), but could of course be
|
|
more like `Namespace::Template<Arg>::Type`.
|
|
|
|
Instead, one could use __initialized_value__ as follows:
|
|
|
|
```
|
|
T var = __initialized_value__;
|
|
```
|
|
|
|
[endsect]
|
|
[endsect]
|
|
|
|
[#sec:references]
|
|
[section References]
|
|
|
|
# Bjarne Stroustrup, Gabriel Dos Reis, and J. Stephen Adamczyk wrote various papers,
|
|
proposing to extend the support for brace-enclosed ['initializer lists]
|
|
in C++. This [@https://en.cppreference.com/w/cpp/language/list_initialization feature] has
|
|
now been available since C++11. This would allow a variable `var` of any __DefaultConstructible__ type
|
|
`T` to be ['value-initialized] by doing `T var = {}`. The papers are listed at Bjarne's web page,
|
|
[@http://www.research.att.com/~bs/WG21.html My C++ Standards committee papers].
|
|
|
|
# Scott Meyers, Effective C++, Third Edition, item 6, ['Explicitly disallow the use of
|
|
compiler-generated functions you do not want], [@http://www.aristeia.com/books.html Scott Meyers: Books and CDs]
|
|
|
|
# The C++ Standard, Second edition (2003), ISO/IEC 14882:2003
|
|
|
|
# POD stands for [@https://en.cppreference.com/w/cpp/named_req/PODType "Plain Old Data"]
|
|
|
|
[endsect]
|
|
|
|
[/===============]
|
|
[xinclude tmp/value_init_reference.xml]
|
|
[/===============]
|
|
|
|
[#sec:acknowledgements]
|
|
[section Acknowledgements]
|
|
|
|
__value_initialized__ was developed by Fernando Cacciola, with help and suggestions
|
|
from David Abrahams and Darin Adler.
|
|
|
|
Special thanks to Bjorn Karlsson who carefully edited and completed this documentation.
|
|
|
|
__value_initialized__ was reimplemented by Fernando Cacciola and Niels Dekker
|
|
for Boost release version 1.35 (2008), offering a workaround to various compiler issues.
|
|
|
|
`boost::__initialized__` was very much inspired by feedback from Edward Diener and Jeffrey Hellrung.
|
|
|
|
__initialized_value__ was written by Niels Dekker, and added to Boost release version 1.36 (2008).
|
|
|
|
Developed by [@mailto:fernando_cacciola@hotmail.com Fernando Cacciola]. The latest version of
|
|
this file can be found at [@http://www.boost.org www.boost.org].
|
|
|
|
[endsect]
|
|
|
|
[endsect]
|