From 1a78a62555be32868418fe52f8e330c9d0f95d5a Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Tue, 30 Oct 2012 12:57:26 -0700 Subject: Imported Upstream version 1.49.0 --- libs/utility/value_init.htm | 505 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 505 insertions(+) create mode 100644 libs/utility/value_init.htm (limited to 'libs/utility/value_init.htm') diff --git a/libs/utility/value_init.htm b/libs/utility/value_init.htm new file mode 100644 index 0000000000..4ae9177604 --- /dev/null +++ b/libs/utility/value_init.htm @@ -0,0 +1,505 @@ + + + + + value_initialized + + + + +

+ Header <boost/utility/value_init.hpp> +

+ +

Contents

+ +
+
Rationale
+
Introduction
+
Details
+
+ + + +
+
Types and objects
+
+ + + Acknowledgements
+
+ +
+

Rationale

+ +

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. +
+

+ +

Introduction

+ +

+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 usually won't 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, the syntax is not allowed for a class that has an explicitly declared +constructor. (But watch out for an upcoming C++ language change, +by Bjarne Stroustrup et al [1]!) +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 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 [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 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. + +

+ +

Details

+

The C++ standard [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 + POD [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 §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 ()
+ +

value-initialization

+ +

The first Technical + Corrigendum for the C++ Standard (TC1), whose draft was released to + the public in November 2001, introduced Core + Issue 178 (among many other issues, of course).

+ +

That issue introduced the new concept of value-initialization + (it 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 won't 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 intializer 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
+ +

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 doesn't 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.

+ +

compiler issues

+ +Various compilers haven't 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's 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: +

+Note that all known GCC issues regarding value-initialization are +fixed with GCC version 4.4, including +GCC Bug 30111. +Clang also has completely implemented value-initialization, as far as we know, +now that 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 +compiler defect macro BOOST_NO_COMPLETE_VALUE_INITIALIZATION. +

+ +

Types and objects

+ +

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<T>& ); +
+
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() ; +
} +
+
} // namespace boost +
+ +

An object of this template class is a T-wrapper convertible + to 'T&' whose wrapped object (data member of type T) + is 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 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 + didn't 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. +
+

+ +

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
+ +

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( value_initialized<T>& ); +
+
private : +
unspecified x ; +
} ; +
+
template<class T> +
T const& get ( initialized<T> const& x ); +
+
template<class T> +
T& get ( initialized<T>& x ); +
+
} // 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. +

+ + +

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 ;
+
+ +

References

+ [1] Bjarne Stroustrup, Gabriel Dos Reis, and J. Stephen Adamczyk wrote + various papers, proposing to extend the support for brace-enclosed initializer lists + in the next version of C++. + 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, + My C++ Standards committee papers
+ [2] Scott Meyers, Effective C++, Third Edition, item 6, + Explicitly disallow the use of compiler-generated functions you do not want, + Scott Meyers: Books and CDs
+ [3] The C++ Standard, Second edition (2003), ISO/IEC 14882:2003
+ [4] POD stands for "Plain Old Data" + +

Acknowledgements

+ value_initialized was developed by Fernando Cacciola, with help and +suggestions from David Abrahams and Darin Adler.
+Special thanks to Björn 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 Fernando Cacciola, + the latest version of this file can be found at www.boost.org. +

+ +
+

Revised 30 May 2010

+ +

© Copyright Fernando Cacciola, 2002 - 2010.

+ +

Distributed under the Boost Software License, Version 1.0. See +www.boost.org/LICENSE_1_0.txt

+ +
+
+ + + -- cgit v1.2.3