development.qbk

来自「Boost provides free peer-reviewed portab」· QBK 代码 · 共 252 行

QBK
252
字号
[/    Boost.Optional    Copyright (c) 2003-2007 Fernando Luis Cacciola Carballal    Distributed under the Boost Software License, Version 1.0.    (See accompanying file LICENSE_1_0.txt or copy at    http://www.boost.org/LICENSE_1_0.txt)][section Development][section The models]In C++, we can ['declare] an object (a variable) of type `T`, and we can give thisvariable an ['initial value] (through an ['initializer]. (c.f. 8.5)).When a declaration includes a non-empty initializer (an initial value is given),it is said that the object has been initialized.If the declaration uses an empty initializer (no initial value is given), andneither default nor value initialization applies, it is said that the object is[*uninitialized]. Its actual value exist but has an ['indeterminate initial value](c.f. 8.5.9).`optional<T>` intends to formalize the notion of initialization (or lack of it)allowing a program to test whether an object has been initialized and statingthat access to the value of an uninitialized object is undefined behavior. Thatis, when a variable is declared as `optional<T>` and no initial value is given,the variable is ['formally] uninitialized. A formally uninitialized optional objecthas conceptually no value at all and this situation can be tested at runtime. Itis formally ['undefined behavior] to try to access the value of an uninitializedoptional. An uninitialized optional can be assigned a value, in which case its initialization state changes to initialized. Furthermore, given the formaltreatment of initialization states in optional objects, it is even possible toreset an optional to ['uninitialized].In C++ there is no formal notion of uninitialized objects, which means thatobjects always have an initial value even if indeterminate.As discussed on the previous section, this has a drawback because you needadditional information to tell if an object has been effectively initialized.One of the typical ways in which this has been historically dealt with is viaa special value: `EOF`, `npos`, -1, etc... This is equivalent to adding thespecial value to the set of possible values of a given type. This super set of`T` plus some ['nil_t]—were `nil_t` is some stateless POD-can be modeled in modernlanguages as a [*discriminated union] of T and nil_t. Discriminated unions areoften called ['variants]. A variant has a ['current type], which in our case is either`T` or `nil_t`.Using the __BOOST_VARIANT__ library, this model can be implemented in terms of `boost::variant<T,nil_t>`.There is precedent for a discriminated union as a model for an optional value:the __HASKELL__ [*Maybe] built-in type constructor. Thus, a discriminated union`T+nil_t` serves as a conceptual foundation.A `variant<T,nil_t>` follows naturally from the traditional idiom of extendingthe range of possible values adding an additional sentinel value with thespecial meaning of ['Nothing].  However, this additional ['Nothing] value is largelyirrelevant for our purpose since our goal is to formalize the notion ofuninitialized objects and, while a special extended value can be used to conveythat meaning, it is not strictly necessary in order to do so.The observation made in the last paragraph about the irrelevant nature of theadditional `nil_t` with respect to [_purpose] of `optional<T>` suggests an alternative model: a ['container] that either has a value of `T` or nothing.As of this writing I don't know of any precedence for a variable-sizefixed-capacity (of 1) stack-based container model for optional values, yet Ibelieve this is the consequence of the lack of practical implementations ofsuch a container rather than an inherent shortcoming of the container model.In any event, both the discriminated-union or the single-element containermodels serve as a conceptual ground for a class representing optional—i.e.possibly uninitialized—objects.For instance, these models show the ['exact] semantics required for a wrapperof optional values:Discriminated-union:* [*deep-copy] semantics: copies of the variant implies copies of the value.* [*deep-relational] semantics: comparisons between variants matches bothcurrent types and values* If the variant's current type is `T`, it is modeling an ['initialized] optional.* If the variant's current type is not `T`, it is modeling an ['uninitialized]optional.* Testing if the variant's current type is `T` models testing if the optionalis initialized* Trying to extract a `T` from a variant when its current type is not `T`, modelsthe undefined behavior of trying to access the value of an uninitialized optionalSingle-element container:* [*deep-copy] semantics: copies of the container implies copies of the value.* [*deep-relational] semantics: comparisons between containers compare containersize and if match, contained value* If the container is not empty (contains an object of type `T`), it is modelingan ['initialized] optional.* If the container is empty, it is modeling an ['uninitialized] optional.* Testing if the container is empty models testing if the optional isinitialized* Trying to extract a `T` from an empty container models the undefined behaviorof trying to access the value of an uninitialized optional[endsect][section The semantics]Objects of type `optional<T>` are intended to be used in places where objects oftype `T` would but which might be uninitialized. Hence, `optional<T>`'s purpose isto formalize the additional possibly uninitialized state.From the perspective of this role, `optional<T>` can have the same operationalsemantics of `T` plus the additional semantics corresponding to this specialstate.As such, `optional<T>` could be thought of as a ['supertype] of `T`. Of course, we can't do that in C++, so we need to compose the desired semantics using adifferent mechanism.Doing it the other way around, that is, making `optional<T>` a ['subtype] of `T`is not only conceptually wrong but also impractical: it is not allowed toderive from a non-class type, such as a built-in type.We can draw from the purpose of `optional<T>` the required basic semantics:* [*Default Construction:] To introduce a formally uninitialized wrappedobject.* [*Direct Value Construction via copy:] To introduce a formally initializedwrapped object whose value is obtained as a copy of some object.* [*Deep Copy Construction:] To obtain a new yet equivalent wrapped object.* [*Direct Value Assignment (upon initialized):] To assign a value to thewrapped object.* [*Direct Value Assignment (upon uninitialized):] To initialize the wrappedobject with a value obtained as a copy of some object.* [*Assignment (upon initialized):] To assign to the wrapped object the valueof another wrapped object.* [*Assignment (upon uninitialized):] To initialize the wrapped object withvalue of another wrapped object.* [*Deep Relational Operations (when supported by the type T):] To comparewrapped object values taking into account the presence of uninitialized states.* [*Value access:] To unwrap the wrapped object.* [*Initialization state query:] To determine if the object is formallyinitialized or not.* [*Swap:] To exchange wrapped objects. (with whatever exception safetyguarantees are provided by `T`'s swap).* [*De-initialization:] To release the wrapped object (if any) and leave thewrapper in the uninitialized state.Additional operations are useful, such as converting constructors andconverting assignments, in-place construction and assignment, and safevalue access via a pointer to the wrapped object or null.[endsect][section The Interface]Since the purpose of optional is to allow us to use objects with a formaluninitialized additional state, the interface could try to follow theinterface of the underlying `T` type as much as possible. In order to choosethe proper degree of adoption of the native `T` interface, the following mustbe noted: Even if all the operations supported by an instance of type `T` aredefined for the entire range of values for such a type, an `optional<T>`extends such a set of values with a new value for which most(otherwise valid) operations are not defined in terms of `T`.Furthermore, since `optional<T>` itself is merely a `T` wrapper (modeling a `T`supertype), any attempt to define such operations upon uninitialized optionalswill be totally artificial w.r.t. `T`.This library chooses an interface which follows from `T`'s interface only forthose operations which are well defined (w.r.t the type `T`) even if any of theoperands are uninitialized. These operations include: construction,copy-construction, assignment, swap and relational operations.For the value access operations, which are undefined (w.r.t the type `T`) whenthe operand is uninitialized, a different interface is chosen (which will beexplained next).Also, the presence of the possibly uninitialized state requires additionaloperations not provided by `T` itself which are supported by a special interface.[heading Lexically-hinted Value Access in the presence of possiblyuntitialized optional objects: The operators * and ->]A relevant feature of a pointer is that it can have a [*null pointer value].This is a ['special] value which is used to indicate that the pointer is notreferring to any object at all. In other words, null pointer values conveythe notion of inexistent objects.This meaning of the null pointer value allowed pointers to became a ['defacto] standard for handling optional objects because all you have to doto refer to a value which you don't really have is to use a null pointervalue of the appropriate type. Pointers have been used for decades—fromthe days of C APIs to modern C++ libraries—to ['refer] to optional (that is,possibly inexistent) objects; particularly as optional arguments to afunction, but also quite often as optional data members.The possible presence of a null pointer value makes the operations thataccess the pointee's value possibly undefined, therefore, expressions whichuse dereference and access operators, such as: `( *p = 2 )` and `( p->foo() )`,implicitly convey the notion of optionality, and this information is tied tothe ['syntax] of the expressions. That is, the presence of operators `*` and `->`tell by themselves —without any additional context— that the expression willbe undefined unless the implied pointee actually exist.Such a ['de facto] idiom for referring to optional objects can be formalizedin the form of a concept: the __OPTIONAL_POINTEE__ concept.This concept captures the syntactic usage of operators `*`, `->` andconversion to `bool` to convey the notion of optionality.However, pointers are good to [_refer] to optional objects, but not particularlygood to handle the optional objects in all other respects, such as initializingor moving/copying them. The problem resides in the shallow-copy of pointersemantics: if you need to effectively move or copy the object, pointers aloneare not enough. The problem is that copies of pointers do not imply copies ofpointees. For example, as was discussed in the motivation, pointers alonecannot be used to return optional objects from a function because the objectmust move outside from the function and into the caller's context.A solution to the shallow-copy problem that is often used is to resort todynamic allocation and use a smart pointer to automatically handle the detailsof this. For example, if a function is to optionally return an object `X`, it canuse `shared_ptr<X>` as the return value. However, this requires dynamic allocationof `X`. If `X` is a built-in or small POD, this technique is very poor in terms ofrequired resources. Optional objects are essentially values so it is veryconvenient to be able to use automatic storage and deep-copy semantics tomanipulate optional values just as we do with ordinary values. Pointers donot have this semantics, so are inappropriate for the initialization andtransport of optional values, yet are quite convenient for handling the accessto the possible undefined value because of the idiomatic aid present in the__OPTIONAL_POINTEE__ concept incarnated by pointers.[heading Optional<T> as a model of OptionalPointee]For value access operations `optional<>` uses operators `*` and `->` tolexically warn about the possibly uninitialized state appealing to thefamiliar pointer semantics w.r.t. to null pointers.[warningHowever, it is particularly important to note that `optional<>` objectsare not pointers. [_`optional<>` is not, and does not model, a pointer].]For instance, `optional<>` does not have shallow-copy so does not alias:two different optionals never refer to the ['same] value unless `T` itself isa reference (but may have ['equivalent] values).The difference between an `optional<T>` and a pointer must be kept in mind,particularly because the semantics of relational operators are different:since `optional<T>` is a value-wrapper, relational operators are deep: theycompare optional values; but relational operators for pointers are shallow:they do not compare pointee values.As a result, you might be able to replace `optional<T>` by `T*` on somesituations but not always. Specifically, on generic code written for both,you cannot use relational operators directly, and must use the templatefunctions __FUNCTION_EQUAL_POINTEES__ and __FUNCTION_LESS_POINTEES__ instead.[endsect][endsect]

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?