diff --git a/value_init.htm b/value_init.htm index e9056c2..d9eb9e5 100644 --- a/value_init.htm +++ b/value_init.htm @@ -1,255 +1,219 @@ - - - - - - - - -Header - - - -

Header <boost/utility/value_init.hpp> -

-

Contents

-
-
Introduction
-
- -
-
Types
-
- -
-

Introduction

-

The C++ standard document realeased by 1998 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 types -are zero-initialized while 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 determines that the object shall be default-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 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 darft was -released to the public on Nov, 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 on some cases non static data members and base class sub-objects are also -value-initialized. The difference is that an object which is value-initialized -won't have (or at least it 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: ().

-

(but recall that the released official Std document says that '()' -invokes default-initialization, not value-initialization as it is now)

-

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

value-initialization syntax

-

Value initialization is specified using (). However, the empty set of -parentheses is not permited by the syntax of the initializer because it is -parsed as the declaration of a function taking no arguments:

-
int x() ; // declares function int(*)()
-int y ( int() ) ; // decalares function int(*)( 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 our 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 supplied by the value_initialized<> template -class.

-

Types

-

template class -value_initialized<T>

-
namespace boost {
-
-template<class T>
-class value_initialized
-{
-  public :
-
-    value_initialized() : x() {}
-
-    operator T&() const { return x ; }
-
-    T& data() const { return x ; }
-
-  private :
-
-    impll-defined 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 friend function get(): -

-
void watch(int);
-value_initialized<int> x;
-
-watch(x) ; // operator T& used.
-watch(x.data());
-watch( get(x) ) // friend function get() used
- -

Both const and non-const objects can be wrapped. Non-constant -objects can be modified directly from within the wrapper but constant objects -cannot:

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

-

Both the conversion operator and the data() member function are -const in order to allow access to the wrapped object from a -constant wrapper:

-
void foo(int);
-value_initialized<int> const x ;
-foo(x);
-
- -

But notice that this conversion operator is to T& but it is -itself const. As a consequence, if T is a non-const type, you can -modify the wrapped object even from within a constant wrapper:

-
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 behaviour is that some commonly used compilers -just don't accept the following valid code:

-
-struct X
-{
-  operator int&() ;
-  operator int const&() const ;   
-};
-X x ;
-(x == 1 ) ; // ERROR HERE!
- -

These compilers complain about ambiguity between the conversion operators. -
-This is strictly wrong, but the only workaround that I know about is to provide -only one of them, which leads to the obscure behaviour just explained.

-
-

Recomended practice: the non-member non-friend get() idiom

-

The obscure behaviour just warned about being able to modify a non-const -wrapped object from within a constant wrapper can be avoided if access to the -wrapped object is always done through 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
-
- -
-

Revised 23 August 2002

-

© Copyright boost.org 2002. Permission to copy, use, modify, sell and -distribute this document is granted provided this copyright notice appears in -all copies. This document is provided "as is" without express or -implied warranty, and with no claim as to its suitability for any purpose.

-

Developed by Fernando Cacciola, -the latest version of this file can be found at www.boost.org, and the boost discussion list at -www.yahoogroups.com/list/boost. -

- - - + + + + + value_initialized + + + + +

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

+ +

Contents

+ +
+
Rationale
+
Introduction
+
+ + + +
+
Types
+
+ + + 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. value_initialized provides +a solution with consistent syntax for value initialization of scalar, +union and class types.
+

+ +

Introduction

+ +

The C++ standard [1] 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 [2] types are zero-initialized, while 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 default-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 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: ().

+ +

(but recall that the current C++ Standard states that '()' invokes default-initialization, +not value-initialization)

+ +

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

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(*)()
int y ( int() ) ; // decalares function int(*)( 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 supplied by the value_initialized<> template + class.

+ +

Types

+ +

template class value_initialized<T>

+ +
namespace boost {

template<class T>
class value_initialized
{
public :
value_initialized() : x() {}
operator T&() const { return x ; }
T& data() const { return x ; }

private :
impll-defined 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:

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

+ +

Both the conversion operator and the data() member function + are const in order to allow access to the wrapped object +from a constant wrapper:

+ +
void foo(int);
value_initialized<int> const x ;
foo(x);
+ +

But notice that this conversion operator is to T& although + it is itself const. As a consequence, if T is + a non-const type, you can modify the wrapped object even from + within a constant wrapper:

+ +
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 is that some commonly used compilers + just don't accept the following valid code:

+ +
struct X
{
operator int&() ;
operator int const&() const ;
};
X x ;
(x == 1 ) ; // ERROR HERE!
+ +

These compilers complain about ambiguity between the conversion operators. + This complaint is incorrect, but the only workaround that I know of is + to provide only one of them, which leads to the obscure behavior just explained.
+

+ +

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

References

+ [1] The C++ Standard, ISO/IEC 14882:98
+ [2] 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. +
 
+ +
+

Revised 19 September 2002

+ +

© Copyright boost.org 2002. Permission to copy, use, modify, sell +and distribute this document is granted provided this copyright notice appears +in all copies. This document is provided "as is" without express or implied +warranty, and with no claim as to its suitability for any purpose.

+ +

Developed by Fernando Cacciola, + the latest version of this file can be found at www.boost.org, and the boost discussion list +at www.yahoogroups.com/list/boost. +

+
+
+ + +