This paper proposes to replace all use of unspecified-bool-type with explicit bool conversion operators. This will simplify the library clauses and aid clarity by removing pseudo-code from normative signatures and simpliying the associated explanatory text. In particular, note that hole in the type system that 20.5.14.2.6 [func.wrap.func.undef] works around is closed, removing the whole clause.
On the other hand, it deliberately leaves the operator bool() conversion function for the vector<bool> and bitset proxy classes as implicit.
While this paper has no suggested applications of the feature beyond boolean conversions, the committee might want to explore explicit pointer conversion operators for the library smart pointers.
The paper would also resolve LWG issues 644 and 686 as NAD.
(A couple of Minor editorial nits (typos etc) are also fixed without comment.)
Change 19.4.2.1 [sys.errcode.overview]
namespace std { class error_code { public: ... // observers: value_type value() const; const error_category& category() const; posix_errno posix() const; string message() const; wstring wmessage() const; explicit operatorunspecified-bool-type() const; ... }; } // namespace std ... explicit operatorunspecified-bool-type() const;
-11- Returns: If value() != value_type(), returns a value that will evaluate true
in a boolean context; otherwise,
returs a value that will evaluate false
. The return type shall not be convertible to int.
-12- Throws: Nothing.
-13- [ Note: This conversion can be used in contexts where a bool is expected (e.g., an if condition); however, implicit
conversions (e.g., to int) that can occur with bool are not allowed, eliminating some sources of user error. One
possible implementation choice for this type is pointer to member. - end note ]
Change 20.5.14.2 [func.wrap.func]:
namespace std { template<class> class function; // undefined template<class R, class... ArgTypes> class function<R(ArgTypes...)> : public unary_function<T1, R> // iff sizeof...(ArgTypes) == 1 and ArgTypes contains T1 : public binary_function<T1, T2, R> // iff sizeof...(ArgTypes) == 2 and ArgTypes contains T1 and T2 { public: ... // 20.5.14.2.3, function capacity: explicit operatorunspecified-bool-type() const; ...private: // 20.5.14.2.6, undefined operators: template<class R2, class... ArgTypes2> bool operator==(const function<R2(ArgTypes2...)>&); template<class R2, class... ArgTypes2> bool operator!=(const function<R2(ArgTypes2...)>&);}; } // namespace std
and 20.5.14.2.3 [func.wrap.func.cap]:
explicit operatorunspecified-bool-type() const
-1- Returns: if *this has a target, returns a value that will evaluate true
in a boolean context; otherwise, returns a value that will evaluate false
in a boolean context. The value type returned shall not be convertible to int.
-2- Throws: nothing.
-3- [ Note: This conversion can be used in contexts where a bool is expected (e.g., an if condition); however, implicit conversions (e.g., to int) that can occur with bool are not allowed, eliminating some sources of user error. One possible implementation choice for this type is pointer-to-member. - end note ]
and remove 20.5.14.2.6 [func.wrap.func.undef]:
template<class R2, class... ArgTypes2> bool operator==(const function<R2(ArgTypes2...)>&); template<class R2, class... ArgTypes2> bool operator!=(const function<R2(ArgTypes2...)>&);
-1- These member functions shall be left undefined.
-2- [ Note: the boolean-like conversion opens a loophole whereby two function instances can be compared via or !=. These undefined void operators close the loophole and ensure a compile-time error. - end note ]
Change 20.6.5.2 [unique.ptr.single]:
template <class T, class D = default_delete<T>> class unique_ptr { ... // observers T& operator*() const; T* operator->() const; T* get() const; deleter_type& get_deleter(); const deleter_type& get_deleter() const; explicit operatorunspecified-bool-type() const; ... };
and 20.6.5.2.4 [unique.ptr.single.observers]
explicit operatorunspecified-bool-type() const;
-11- Returns: An unspecified value that, when used in boolean contexts, is equivalent to get() != 0
.
-12- Throws: nothing.
-13- [ Note: The unspecified-bool-type is often implemented as a pointer to a private data member, avoiding many of the implicit conversion pitfalls. - end note ]
and 20.6.5.3 [unique.ptr.runtime]:
template <class T, class D> class unique_ptr<T[], D> { ... // observers T& operator[]() const; T* get() const; deleter_type& get_deleter(); const deleter_type& get_deleter() const; explicit operatorunspecified-bool-type() const; ... };
and 20.6.5.4 [unique.ptr.compiletime]:
template <class T, class D, size_t N> class unique_ptr<T[N], D> { ... // observers T& operator[]() const; T* get() const; deleter_type& get_deleter(); const deleter_type& get_deleter() const; explicit operatorunspecified-bool-type() const; ... };
Change 20.6.6.2 [util.smartptr.shared]:
namespace std { templateclass shared_ptr { ... // 20.6.6.2.5, observers: T* get() const; T& operator*() const; T* operator->() const; long use_count() const; bool unique() const; explicit operator unspecified-bool-type() const; }; } // namespace std
and 20.6.6.2.5 [util.smartptr.shared.obs]
explicit operatorunspecified-bool-type() const;
-16- Returns: an unspecified value that, when used in boolean contexts, is equivalent to get() != 0
.
-17- Throws: nothing.
-18- [ Note: This conversion operator allows shared_ptr objects to be used in boolean contexts. [ Example: if (p
&& p->valid()) - end example ] One possible choice for the return type is a pointer to member function, which avoids many of the implicit conversion pitfalls of a bool or void* return type. - end note ]
Change 27.4.4 [ios]
namespace std { template <class charT, class traits = char_traits<charT> > class basic_ios : public ios_base { public: ... explicit operatorunspecified-bool-type() const; }; }
and 27.4.4.3 [iostate.flags]:
explicit operatorunspecified-bool-type() const;
-1- Returns: If !fail()
then a value that will evaluate false
in a boolean context; otherwise a value that will evaluate true
in a boolean context. The value type returned shall not be convertible to int.
[ Note: This conversion can be used in contexts where a bool is expected (e.g., an if condition); however, implicit conversions (e.g., to int) that can occur with bool are not allowed, eliminating some sources of user error. One
possible implementation choice for this type is pointer-to-member. - end note ]
Change 27.6.1.1.3 [istream::sentry]
namespace std { template <class charT,class traits = char_traits<charT> > class basic_istream<charT,traits>::sentry { typedef traits traits_type; // bool ok_; exposition only public: explicit sentry(basic_istream<charT,traits>& is , bool noskipws = false); ~sentry(); explicit operator bool() const { return ok_; } private: sentry(const sentry&); // not defined sentry& operator=(const sentry&); // not defined }; }
Change 27.6.2.4 [ostream::sentry]
namespace std { template <class charT,class traits = char_traits<charT> > class basic_ostream<charT,traits>::sentry { // bool ok_; exposition only public: explicit sentry(basic_ostream<charT,traits>& os); ~sentry(); explicit operator bool() const { return ok_; } private: sentry(const sentry&); // not defined sentry& operator=(const sentry&); // not defined }; }