diff --git a/value_init.htm b/value_init.htm index 8ceba8b..405f0b6 100644 --- a/value_init.htm +++ b/value_init.htm @@ -17,11 +17,13 @@
Rationale
Introduction
+
Details
@@ -30,7 +32,7 @@ Acknowledgements
@@ -44,24 +46,91 @@ 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 +this problem must be addressed. The template value_initialized provides a solution with consistent syntax for value initialization of scalar, -union and class types.
+union and class types. +Moreover, value_initialized offers a workaround to various +compiler issues regarding value-initialization. + +

Introduction

-

The C++ standard [1] contains the definitions +

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

+ +

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 [2] types are zero-initialized, while class + 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 default-initialized (but see below). However, if a declaration +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 for the + 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 ()
@@ -87,14 +156,11 @@ the object shall be default-initialized (but see below). However, if a decla

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

@@ -102,7 +168,7 @@ not value-initialization)

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(*)() )
+
int x() ; // declares function int(*)()

Thus, the empty () must be put in some other initialization context.

@@ -124,8 +190,50 @@ 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.

+

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. +Compilers usually support value-initialization for built-in types properly. +But objects of user-defined types that involve aggregates may in some cases +be partially, or even entirely left uninitialized, when they should be value-initialized. +

+

+We have encountered issues regarding value-initialization on compilers by +Microsoft, Sun, Borland, and GNU. Here is a list of bug reports on those issues: + +
+ +Microsoft Feedback ID 100744 - Value-initialization in new-expression +
Reported by Pavel Kuznetsov (MetaCommunications Engineering), 2005-07-28 +
+ +GCC Bug 30111 - Value-initialization of POD base class doesn't initialize members +
Reported by Jonathan Wakely, 2006-12-07 +
+ +GCC Bug 33916 - Default constructor fails to initialize array members +
Reported by Michael Elizabeth Chastain, 2007-10-26 +
+ +Borland Report 51854 - Value-initialization: POD struct should be zero-initialized +
Reported by Niels Dekker (LKEB, Leiden University Medical Center), 2007-09-11 +
+
+

+New versions of value_initialized +(Boost release version 1.35 or higher) +offer a workaround to these issues: value_initialized will now clear +its internal data, prior to constructing the object that it contains. +

Types

@@ -191,23 +299,36 @@ 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 + [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. - +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. +

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. + href="http://www.boost.org">www.boost.org.


-

Revised 19 September 2002

+

Revised 15 January 2008

-

© Copyright Fernando Cacciola, 2002.

+

© Copyright Fernando Cacciola, 2002, 2008.

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 4c576a6..7b332d7 100644 --- a/value_init_test.cpp +++ b/value_init_test.cpp @@ -1,4 +1,4 @@ -// Copyright 2002, Fernando Luis Cacciola Carballal. +// Copyright 2002-2008, Fernando Luis Cacciola Carballal. // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -6,12 +6,15 @@ // // Test program for "boost/utility/value_init.hpp" // -// Initial: 21 Agu 2002 +// 21 Agu 2002 (Created) Fernando Cacciola +// 15 Jan 2008 (Added tests regarding compiler issues) Fernando Cacciola, Niels Dekker +#include // For memcmp. #include #include #include "boost/utility/value_init.hpp" +#include #ifdef __BORLANDC__ #pragma hdrstop @@ -62,6 +65,9 @@ struct NonPOD : NonPODBase // // Sample aggregate POD struct type +// Some compilers do not correctly value-initialize such a struct, for example: +// Borland C++ Report #51854, "Value-initialization: POD struct should be zero-initialized " +// http://qc.codegear.com/wc/qcmain.aspx?d=51854 // struct AggregatePODStruct { @@ -73,6 +79,106 @@ struct AggregatePODStruct bool operator == ( AggregatePODStruct const& lhs, AggregatePODStruct const& rhs ) { return lhs.f == rhs.f && lhs.c == rhs.c && lhs.i == rhs.i ; } +// +// An aggregate struct that contains an std::string and an int. +// Pavel Kuznetsov (MetaCommunications Engineering) used a struct like +// this to reproduce the Microsoft Visual C++ compiler bug, reported as +// Feedback ID 100744, "Value-initialization in new-expression" +// https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=100744 +// +struct StringAndInt +{ + std::string s; + int i; +}; + +bool operator == ( StringAndInt const& lhs, StringAndInt const& rhs ) +{ return lhs.s == rhs.s && lhs.i == rhs.i ; } + + +// +// A struct that has an explicit (user defined) destructor. +// Some compilers do not correctly value-initialize such a struct, for example: +// Microsoft Visual C++, Feedback ID 100744, "Value-initialization in new-expression" +// https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=100744 +// +struct StructWithDestructor +{ + int i; + ~StructWithDestructor() {} +}; + +bool operator == ( StructWithDestructor const& lhs, StructWithDestructor const& rhs ) +{ return lhs.i == rhs.i ; } + + +// +// A struct that has a virtual function. +// Some compilers do not correctly value-initialize such a struct either, for example: +// Microsoft Visual C++, Feedback ID 100744, "Value-initialization in new-expression" +// https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=100744 +// +struct StructWithVirtualFunction +{ + int i; + virtual void VirtualFunction(); +}; + +void StructWithVirtualFunction::VirtualFunction() +{ +} + +bool operator == ( StructWithVirtualFunction const& lhs, StructWithVirtualFunction const& rhs ) +{ return lhs.i == rhs.i ; } + + +// +// A struct that is derived from an aggregate POD struct. +// Some compilers do not correctly value-initialize such a struct, for example: +// GCC Bugzilla Bug 30111, "Value-initialization of POD base class doesn't initialize members", +// reported by Jonathan Wakely, http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30111 +// +struct DerivedFromAggregatePODStruct : AggregatePODStruct +{ + DerivedFromAggregatePODStruct() : AggregatePODStruct() {} +}; + +// +// A struct that wraps an aggregate POD struct as data member. +// +struct AggregatePODStructWrapper +{ + AggregatePODStructWrapper() : dataMember() {} + AggregatePODStruct dataMember; +}; + +bool operator == ( AggregatePODStructWrapper const& lhs, AggregatePODStructWrapper const& rhs ) +{ return lhs.dataMember == rhs.dataMember ; } + +typedef unsigned char ArrayOfBytes[256]; + + +// +// A struct that allows testing whether the appropriate copy functions are called. +// +struct CopyFunctionCallTester +{ + bool is_copy_constructed; + bool is_assignment_called; + + CopyFunctionCallTester() + : is_copy_constructed(false), is_assignment_called(false) {} + + CopyFunctionCallTester(const CopyFunctionCallTester & ) + : is_copy_constructed(true), is_assignment_called(false) {} + + CopyFunctionCallTester & operator=(const CopyFunctionCallTester & ) + { + is_assignment_called = true ; + return *this ; + } +}; + // // This test function tests boost::value_initialized for a specific type T. @@ -97,6 +203,16 @@ bool test ( T const& y, T const& z ) x_c_ref = z ; BOOST_CHECK ( x_c == z ) ; + boost::value_initialized const copy1 = x; + BOOST_CHECK ( boost::get(copy1) == boost::get(x) ) ; + + boost::value_initialized copy2; + copy2 = x; + BOOST_CHECK ( boost::get(copy2) == boost::get(x) ) ; + + boost::shared_ptr > ptr( new boost::value_initialized ); + BOOST_CHECK ( y == *ptr ) ; + #if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) boost::value_initialized cx ; BOOST_CHECK ( y == cx ) ; @@ -106,6 +222,7 @@ bool test ( T const& y, T const& z ) BOOST_CHECK ( y == cx_c ) ; BOOST_CHECK ( y == boost::get(cx_c) ) ; #endif + return boost::minimal_test::errors_counter() == counter_before_test ; } @@ -123,13 +240,57 @@ int test_main(int, char **) AggregatePODStruct nonZeroInitializedAggregatePODStruct = { 1.25f, 'a', -1 }; BOOST_CHECK ( test(zeroInitializedAggregatePODStruct, nonZeroInitializedAggregatePODStruct) ); + StringAndInt stringAndInt0; + StringAndInt stringAndInt1; + stringAndInt0.i = 0; + stringAndInt1.i = 1; + stringAndInt1.s = std::string("1"); + BOOST_CHECK ( test(stringAndInt0, stringAndInt1) ); + + StructWithDestructor structWithDestructor0; + StructWithDestructor structWithDestructor1; + structWithDestructor0.i = 0; + structWithDestructor1.i = 1; + BOOST_CHECK ( test(structWithDestructor0, structWithDestructor1) ); + + StructWithVirtualFunction structWithVirtualFunction0; + StructWithVirtualFunction structWithVirtualFunction1; + structWithVirtualFunction0.i = 0; + structWithVirtualFunction1.i = 1; + BOOST_CHECK ( test(structWithVirtualFunction0, structWithVirtualFunction1) ); + + DerivedFromAggregatePODStruct derivedFromAggregatePODStruct0; + DerivedFromAggregatePODStruct derivedFromAggregatePODStruct1; + static_cast(derivedFromAggregatePODStruct0) = zeroInitializedAggregatePODStruct; + static_cast(derivedFromAggregatePODStruct1) = nonZeroInitializedAggregatePODStruct; + BOOST_CHECK ( test(derivedFromAggregatePODStruct0, derivedFromAggregatePODStruct1) ); + + AggregatePODStructWrapper aggregatePODStructWrapper0; + AggregatePODStructWrapper aggregatePODStructWrapper1; + aggregatePODStructWrapper0.dataMember = zeroInitializedAggregatePODStruct; + aggregatePODStructWrapper1.dataMember = nonZeroInitializedAggregatePODStruct; + BOOST_CHECK ( test(aggregatePODStructWrapper0, aggregatePODStructWrapper1) ); + + ArrayOfBytes zeroInitializedArrayOfBytes = { 0 }; + boost::value_initialized valueInitializedArrayOfBytes; + BOOST_CHECK (std::memcmp(get(valueInitializedArrayOfBytes), zeroInitializedArrayOfBytes, sizeof(ArrayOfBytes)) == 0); + + boost::value_initialized copyFunctionCallTester1; + BOOST_CHECK ( ! get(copyFunctionCallTester1).is_copy_constructed); + BOOST_CHECK ( ! get(copyFunctionCallTester1).is_assignment_called); + + boost::value_initialized copyFunctionCallTester2 = boost::value_initialized(copyFunctionCallTester1); + BOOST_CHECK ( get(copyFunctionCallTester2).is_copy_constructed); + BOOST_CHECK ( ! get(copyFunctionCallTester2).is_assignment_called); + + boost::value_initialized copyFunctionCallTester3; + copyFunctionCallTester3 = boost::value_initialized(copyFunctionCallTester1); + BOOST_CHECK ( ! get(copyFunctionCallTester3).is_copy_constructed); + BOOST_CHECK ( get(copyFunctionCallTester3).is_assignment_called); + return 0; } unsigned int expected_failures = 0; - - - -