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 @@ - - - - -
- - - -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 ()- -
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 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.
-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- -
-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 -itselfconst
. 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.
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<>
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.
+
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 ()
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 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.
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
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.
+
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
+ +
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. +
+