diff --git a/include/boost/utility/value_init.hpp b/include/boost/utility/value_init.hpp new file mode 100644 index 0000000..d1f7fd3 --- /dev/null +++ b/include/boost/utility/value_init.hpp @@ -0,0 +1,82 @@ +// (C) 2002, Fernando Luis Cacciola Carballal. +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// 21 Ago 2002 (Created) Fernando Cacciola +// +#ifndef BOOST_UTILITY_VALUE_INIT_21AGO2002_HPP +#define BOOST_UTILITY_VALUE_INIT_21AGO2002_HPP + +#include "boost/detail/select_type.hpp" +#include "boost/type_traits/cv_traits.hpp" + +namespace boost { + +namespace vinit_detail { + +template +class const_T_base +{ + protected : + + const_T_base() : x() {} + + T x ; +} ; + +template +struct non_const_T_base +{ + protected : + + non_const_T_base() : x() {} + + mutable T x ; +} ; + +template +struct select_base +{ + typedef typename + detail::if_true< ::boost::is_const::value > + ::template then< const_T_base, non_const_T_base >::type type ; +} ; + +} // namespace vinit_detail + +template +class value_initialized : vinit_detail::select_base::type +{ + public : + + value_initialized() {} + + operator T&() const { return this->x ; } + + T& data() const { return this->x ; } + +} ; + +template +T const& get ( value_initialized const& x ) +{ + return x.data() ; +} +template +T& get ( value_initialized& x ) +{ + return x.data() ; +} + +} // namespace boost + + +#endif + diff --git a/value_init.htm b/value_init.htm new file mode 100644 index 0000000..e9056c2 --- /dev/null +++ b/value_init.htm @@ -0,0 +1,255 @@ + + + + + + + + +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. +

+ + + diff --git a/value_init_test.cpp b/value_init_test.cpp new file mode 100644 index 0000000..43ae29c --- /dev/null +++ b/value_init_test.cpp @@ -0,0 +1,119 @@ +// (C) 2002, Fernando Luis Cacciola Carballal. +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// Test program for "boost/utility/value_init.hpp" +// +// Initial: 21 Agu 2002 + +#include +#include + +#include "boost/utility/value_init.hpp" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#define BOOST_INCLUDE_MAIN +#include "boost/test/test_tools.hpp" + +// +// Sample POD type +// +struct POD +{ + POD () : c(0), i(0), f(0) {} + + POD ( char c_, int i_, float f_ ) : c(c_), i(i_), f(f_) {} + + friend std::ostream& operator << ( std::ostream& os, POD const& pod ) + { return os << '(' << pod.c << ',' << pod.i << ',' << pod.f << ')' ; } + + friend bool operator == ( POD const& lhs, POD const& rhs ) + { return lhs.f == rhs.f && lhs.c == rhs.c && lhs.i == rhs.i ; } + + float f; + char c; + int i; +} ; + +// +// Sample non POD type +// +struct NonPODBase +{ + virtual ~NonPODBase() {} +} ; +struct NonPOD : NonPODBase +{ + NonPOD () : id() {} + NonPOD ( std::string const& id_) : id(id_) {} + + friend std::ostream& operator << ( std::ostream& os, NonPOD const& npod ) + { return os << '(' << npod.id << ')' ; } + + friend bool operator == ( NonPOD const& lhs, NonPOD const& rhs ) + { return lhs.id == rhs.id ; } + + std::string id ; +} ; + +template +void test ( T const& y, T const& z ) +{ + boost::value_initialized x ; + BOOST_TEST ( y == x ) ; + BOOST_TEST ( y == get(x) ) ; + static_cast(x) = z ; + get(x) = z ; + BOOST_TEST ( x == z ) ; + + boost::value_initialized const x_c ; + BOOST_TEST ( y == x_c ) ; + BOOST_TEST ( y == get(x_c) ) ; + static_cast(x_c) = z ; + BOOST_TEST ( x_c == z ) ; + #ifdef PRODUCE_ERROR_1 + get(x_c) = z ; // this should produce an ERROR + #endif + + boost::value_initialized cx ; + BOOST_TEST ( y == cx ) ; + BOOST_TEST ( y == get(cx) ) ; + #ifdef PRODUCE_ERROR_2 + get(cx) = z ; // this should produce an ERROR + #endif + + boost::value_initialized const cx_c ; + BOOST_TEST ( y == cx_c ) ; + BOOST_TEST ( y == get(cx_c) ) ; + #ifdef PRODUCE_ERROR_3 + get(cx_c) = z ; // this should produce an ERROR + #endif +} + +int test_main(int, char **) +{ + test( 0,1234 ) ; + test( 0.0,12.34 ) ; + test( POD(0,0,0.0), POD('a',1234,56.78) ) ; + test( NonPOD( std::string() ), NonPOD( std::string("something") ) ) ; + + return 0; +} + + +unsigned int expected_failures = 0; + + + + +