Created on 2007-11-30.00:00:00 last changed 171 months ago
[ post Bellevue: Daniel provided revised wording. ]
Proposed resolution:
The proposed changes in the following revision refers to the current state of N2521 including the assumption that [unique.ptr.compiletime] will be removed according to the current state of 740.
The specialization unique_ptr<T[]> has some more restrictive constraints on type-completeness on T than unique_ptr<T>. The following proposed wordings try to cope with that. If the committee sees less usefulness on relaxed constraints on unique_ptr<T[]>, the alternative would be to stop this relaxation e.g. by adding one further bullet to [unique.ptr.runtime]/1: "T shall be a complete type, if used as template argument of unique_ptr<T[], D>
This issue has some overlap with 673, but it seems not to cause any problems with this one, because 673 adds only optional requirements on D that do not conflict with the here discussed ones, provided that D::pointer's operations (including default construction, copy construction/assignment, and pointer conversion) are specified not to throw, otherwise this would have impact on the current specification of unique_ptr.
In [unique.ptr]/2 add as the last sentence to the existing para:
The unique_ptr provides a semantics of strict ownership. A unique_ptr owns the object it holds a pointer to. A unique_ptr is not CopyConstructible, nor CopyAssignable, however it is MoveConstructible and MoveAssignable. The template parameter T of unique_ptr may be an incomplete type. [ Note: The uses of unique_ptr include providing exception safety for dynamically allcoated memory, passing ownership of dynamically allocated memory to a function, and returning dynamically allocated memory from a function. -- end note ]
[unique.ptr.single.ctor]/1: No changes necessary.
N.B.: We only need the requirement that D is DefaultConstructible. The current wording says just this.
In [unique.ptr.single.ctor]/5 change the requires clause to say:
Requires:
The expression D()(p) shall be well formed. The default constructor of D shall not throw an exception.D must not be a reference type.D shall be default constructible, and that construction shall not throw an exception.N.B.: There is no need that the expression D()(p) is well-formed at this point. I assume that the current wording is based on the corresponding shared_ptr wording. In case of shared_ptr this requirement is necessary, because the corresponding c'tor *can* fail and must invoke delete p/d(p) in this case. Unique_ptr is simpler in this regard. The *only* functions that must insist on well-formedness and well-definedness of the expression get_deleter()(get()) are (1) the destructor and (2) reset. The reasoning for the wording change to explicitly require DefaultConstructible of D is to guarantee that invocation of D's default c'tor is both well-formed and well-defined. Note also that we do *not* need the requirement that T must be complete, also in contrast to shared_ptr. Shared_ptr needs this, because it's c'tor is a template c'tor which potentially requires Convertible<Y*, X*>, which again requires Completeness of Y, if !SameType<X, Y>
Merge [unique.ptr.single.ctor]/12+13 thereby removing the sentence of 12, but transferring the "requires" to 13:
Requires: If D is not an lvalue-reference type then[..]
N.B.: For the same reasons as for (3), there is no need that d(p) is well-formed/well-defined at this point. The current wording guarantees all what we need, namely that the initialization of both the T* pointer and the D deleter are well-formed and well-defined.
[unique.ptr.single.ctor]/21:
Requires: If D is not a reference type, construction of the deleter D from an rvalue of type E shall be well formed and shall not throw an exception. If D is a reference type, then E shall be the same type as D (diagnostic required). U* shall be implicitly convertible to T*. [Note: These requirements imply that T and U be complete types. -- end note]
[unique.ptr.single.dtor]: Just before p1 add a new paragraph:
Requires: The expression get_deleter()(get()) shall be well-formed, shall have well-defined behavior, and shall not throw exceptions. [Note: The use of default_delete requires T to be a complete type. -- end note]
N.B.: This requirement ensures that the whole responsibility on type-completeness of T is delegated to this expression.
[unique.ptr.single.asgn]/1: No changes necessary, except the current editorial issue, that "must shall" has to be changed to "shall", but this change is not a special part of this resolution.
[unique.ptr.single.asgn]/6:
Requires: Assignment of the deleter D from an rvalue D shall not throw an exception. U* shall be implicitly convertible to T*. [Note: These requirements imply that T and U be complete types. -- end note]
[unique.ptr.single.asgn]/11: No changes necessary.
[unique.ptr.single.observers]/1+4+7+9+11:
T* operator->() const;Note: Use typically requires T shall be complete. — end note]
[unique.ptr.single.modifiers]/4: Just before p. 4 add a new paragraph:
Requires: The expression get_deleter()(get()) shall be well-formed, shall have well-defined behavior, and shall not throw exceptions.
[unique.ptr.runtime]: Add one additional bullet on paragraph 1:
A specialization for array types is provided with a slightly altered interface.
- ...
- T shall be a complete type.
[ Bellevue: ]
Move to open. The LWG is comfortable with the intent of allowing incomplete types and making unique_ptr more like shared_ptr, but we are not comfortable with the wording. The specification for unique_ptr should be more like that of shared_ptr. We need to know, for individual member functions, which ones require their types to be complete. The shared_ptr specification is careful to say that for each function, and we need the same level of care here. We also aren't comfortable with the "part of the operational semantic" language; it's not used elsewhere in the standard, and it's not clear what it means. We need a volunteer to produce new wording.
In contrast to the proposed std::shared_ptr, std::unique_ptr does currently not support incomplete types, because it gives no explicit grant - thus instantiating unique_ptr with an incomplete pointee type T automatically belongs to undefined behaviour according to [res.on.functions]/2, last bullet. This is an unnecessary restriction and prevents many well-established patterns - like the bridge pattern - for std::unique_ptr.
History | |||
---|---|---|---|
Date | User | Action | Args |
2010-10-21 18:28:33 | admin | set | messages: + msg3689 |
2010-10-21 18:28:33 | admin | set | messages: + msg3688 |
2010-10-21 18:28:33 | admin | set | messages: + msg3687 |
2007-11-30 00:00:00 | admin | create |