From 0af1959b304c12d38dce5b61835954498273a754 Mon Sep 17 00:00:00 2001 From: Niels Dekker Date: Fri, 20 Feb 2009 20:35:34 +0000 Subject: [PATCH] Updated value_initialized documentation and test following changeset [51355]. [SVN r51356] --- value_init.htm | 68 ++++++++++++++++++++++++++++++--------------- value_init_test.cpp | 2 +- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/value_init.htm b/value_init.htm index 5c1b20e..5c50fd9 100644 --- a/value_init.htm +++ b/value_init.htm @@ -253,7 +253,33 @@ its internal data, prior to constructing the object that it contains.

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 ; }
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
+
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) @@ -271,7 +297,8 @@ its internal data, prior to constructing the object that it contains. 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
+
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 @@ -281,37 +308,34 @@ non-member function get():

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

+

The value_initialized implementation of Boost version 1.38.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:

-
void foo(int);
value_initialized<int> const x ;
foo(x);
+
value_initialized<int> const x_c ;
int& xr = x_c ; // OK, conversion to int& available even though x_c is itself const. +
xr = 2 ;
-

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:

+

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

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 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 can be avoided if access to +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
@@ -383,9 +407,9 @@ for Boost release version 1.35 (2008), offering a workaround to various compiler


-

Revised 28 August 2008

+

Revised 20 February 2009

-

© Copyright Fernando Cacciola, 2002, 2008.

+

© Copyright Fernando Cacciola, 2002, 2009.

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

diff --git a/value_init_test.cpp b/value_init_test.cpp index 63f324d..67ebad8 100644 --- a/value_init_test.cpp +++ b/value_init_test.cpp @@ -260,7 +260,7 @@ bool test ( T const& y, T const& z ) boost::value_initialized const x_c ; BOOST_CHECK ( y == x_c ) ; BOOST_CHECK ( y == boost::get(x_c) ) ; - T& x_c_ref = x_c ; + T& x_c_ref = const_cast( boost::get(x_c) ) ; x_c_ref = z ; BOOST_CHECK ( x_c == z ) ;