https://gcc.gnu.org/g:6d86486292acbeeeda16b4f69455143391845706
commit r15-2309-g6d86486292acbeeeda16b4f69455143391845706 Author: Jonathan Wakely <jwak...@redhat.com> Date: Tue Jul 23 12:45:37 2024 +0100 libstdc++: Use concepts and conditional explicit in std::optional For C++20 mode we can improve compile times by using conditional explicit to reduce the number of constructor overloads. We can also use requires-clauses instead of SFINAE to implement constraints on the constructors and assignment operators. libstdc++-v3/ChangeLog: * include/std/optional (optional): Use C++20 features to simplify overload sets for constructors and assignment operators. Diff: --- libstdc++-v3/include/std/optional | 130 +++++++++++++++++++++++++++++++++++--- 1 file changed, 121 insertions(+), 9 deletions(-) diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index 700e7047abae..2cc0221865e3 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -768,6 +768,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION is_assignable<_Tp&, const optional<_Up>&&>, is_assignable<_Tp&, optional<_Up>&&>>; +#if __cpp_concepts && __cpp_conditional_explicit && __glibcxx_remove_cvref +# define _GLIBCXX_USE_CONSTRAINTS_FOR_OPTIONAL 1 +#endif + /** * @brief Class template for optional values. */ @@ -794,17 +798,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using _Base = _Optional_base<_Tp>; // SFINAE helpers - template<typename _Up> - using __not_self = __not_<is_same<optional, __remove_cvref_t<_Up>>>; - template<typename _Up> - using __not_tag = __not_<is_same<in_place_t, __remove_cvref_t<_Up>>>; - template<typename... _Cond> - using _Requires = enable_if_t<__and_v<_Cond...>, bool>; // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3836. std::expected<bool, E1> conversion constructor // expected(const expected<U, G>&) should take precedence over // expected(U&&) with operator bool +#ifdef _GLIBCXX_USE_CONSTRAINTS_FOR_OPTIONAL + template<typename _From, typename = remove_cv_t<_Tp>> + static constexpr bool __not_constructing_bool_from_optional + = true; + + // If T is cv bool, remove_cvref_t<U> is not a specialization of optional + // i.e. do not initialize a bool from optional<U>::operator bool(). + template<typename _From> + static constexpr bool + __not_constructing_bool_from_optional<_From, bool> + = !__is_optional_v<remove_cvref_t<_From>>; + + // If T is not cv bool, converts-from-any-cvref<T, optional<U>> is false. + // The constructor that converts from optional<U> is disabled if the + // contained value can be initialized from optional<U>, so that the + // optional(U&&) constructor can be used instead. + template<typename _From, typename = remove_cv_t<_Tp>> + static constexpr bool __construct_from_contained_value + = !__converts_from_optional<_Tp, _From>::value; + + // However, optional<U> can always be converted to bool, so don't apply + // this constraint when T is cv bool. + template<typename _From> + static constexpr bool __construct_from_contained_value<_From, bool> + = true; +#else template<typename _From, typename = remove_cv_t<_Tp>> struct __not_constructing_bool_from_optional : true_type @@ -825,6 +849,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : true_type { }; + template<typename _Up> + using __not_self = __not_<is_same<optional, __remove_cvref_t<_Up>>>; + template<typename _Up> + using __not_tag = __not_<is_same<in_place_t, __remove_cvref_t<_Up>>>; + template<typename _Up> + using __is_bool = is_same<remove_cv_t<_Up>, bool>; + template<typename... _Cond> + using _Requires = enable_if_t<__and_v<_Cond...>, bool>; +#endif + public: using value_type = _Tp; @@ -833,6 +867,58 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr optional(nullopt_t) noexcept { } // Converting constructors for engaged optionals. +#ifdef _GLIBCXX_USE_CONSTRAINTS_FOR_OPTIONAL + template<typename _Up = _Tp> + requires (!is_same_v<optional, remove_cvref_t<_Up>>) + && (!is_same_v<in_place_t, remove_cvref_t<_Up>>) + && is_constructible_v<_Tp, _Up> + && __not_constructing_bool_from_optional<_Up> + constexpr explicit(!is_convertible_v<_Up, _Tp>) + optional(_Up&& __t) + noexcept(is_nothrow_constructible_v<_Tp, _Up>) + : _Base(std::in_place, std::forward<_Up>(__t)) { } + + template<typename _Up> + requires (!is_same_v<optional, remove_cvref_t<_Up>>) + && is_constructible_v<_Tp, const _Up&> + && __construct_from_contained_value<_Up> + constexpr explicit(!is_convertible_v<const _Up&, _Tp>) + optional(const optional<_Up>& __t) + noexcept(is_nothrow_constructible_v<_Tp, const _Up&>) + { + if (__t) + emplace(__t._M_get()); + } + + template<typename _Up> + requires (!is_same_v<optional, remove_cvref_t<_Up>>) + && is_constructible_v<_Tp, _Up> + && __construct_from_contained_value<_Up> + constexpr explicit(!is_convertible_v<_Up, _Tp>) + optional(optional<_Up>&& __t) + noexcept(is_nothrow_constructible_v<_Tp, _Up>) + { + if (__t) + emplace(std::move(__t._M_get())); + } + + template<typename... _Args> + requires is_constructible_v<_Tp, _Args...> + explicit constexpr + optional(in_place_t, _Args&&... __args) + noexcept(is_nothrow_constructible_v<_Tp, _Args...>) + : _Base(std::in_place, std::forward<_Args>(__args)...) + { } + + template<typename _Up, typename... _Args> + requires is_constructible_v<_Tp, initializer_list<_Up>&, _Args...> + explicit constexpr + optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args) + noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, + _Args...>) + : _Base(std::in_place, __il, std::forward<_Args>(__args)...) + { } +#else template<typename _Up = _Tp, _Requires<__not_self<_Up>, __not_tag<_Up>, is_constructible<_Tp, _Up>, @@ -921,6 +1007,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>) : _Base(std::in_place, __il, std::forward<_Args>(__args)...) { } +#endif // Assignment operators. _GLIBCXX20_CONSTEXPR optional& @@ -931,13 +1018,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _Up = _Tp> - _GLIBCXX20_CONSTEXPR +#ifdef _GLIBCXX_USE_CONSTRAINTS_FOR_OPTIONAL + requires (!is_same_v<optional, remove_cvref_t<_Up>>) + && (!(is_scalar_v<_Tp> && is_same_v<_Tp, decay_t<_Up>>)) + && is_constructible_v<_Tp, _Up> + && is_assignable_v<_Tp&, _Up> + constexpr optional& +#else enable_if_t<__and_v<__not_self<_Up>, __not_<__and_<is_scalar<_Tp>, is_same<_Tp, decay_t<_Up>>>>, is_constructible<_Tp, _Up>, is_assignable<_Tp&, _Up>>, optional&> +#endif operator=(_Up&& __u) noexcept(__and_v<is_nothrow_constructible<_Tp, _Up>, is_nothrow_assignable<_Tp&, _Up>>) @@ -951,13 +1045,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _Up> - _GLIBCXX20_CONSTEXPR +#ifdef _GLIBCXX_USE_CONSTRAINTS_FOR_OPTIONAL + requires (!is_same_v<optional, remove_cvref_t<_Up>>) + && is_constructible_v<_Tp, const _Up&> + && is_assignable_v<_Tp&, const _Up&> + && (!__converts_from_optional<_Tp, _Up>::value) + && (!__assigns_from_optional<_Tp, _Up>::value) + constexpr optional& +#else enable_if_t<__and_v<__not_<is_same<_Tp, _Up>>, is_constructible<_Tp, const _Up&>, is_assignable<_Tp&, const _Up&>, __not_<__converts_from_optional<_Tp, _Up>>, __not_<__assigns_from_optional<_Tp, _Up>>>, optional&> +#endif operator=(const optional<_Up>& __u) noexcept(__and_v<is_nothrow_constructible<_Tp, const _Up&>, is_nothrow_assignable<_Tp&, const _Up&>>) @@ -977,13 +1079,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _Up> - _GLIBCXX20_CONSTEXPR +#ifdef _GLIBCXX_USE_CONSTRAINTS_FOR_OPTIONAL + requires (!is_same_v<optional, remove_cvref_t<_Up>>) + && is_constructible_v<_Tp, _Up> + && is_assignable_v<_Tp&, _Up> + && (!__converts_from_optional<_Tp, _Up>::value) + && (!__assigns_from_optional<_Tp, _Up>::value) + constexpr optional& +#else enable_if_t<__and_v<__not_<is_same<_Tp, _Up>>, is_constructible<_Tp, _Up>, is_assignable<_Tp&, _Up>, __not_<__converts_from_optional<_Tp, _Up>>, __not_<__assigns_from_optional<_Tp, _Up>>>, optional&> +#endif operator=(optional<_Up>&& __u) noexcept(__and_v<is_nothrow_constructible<_Tp, _Up>, is_nothrow_assignable<_Tp&, _Up>>) @@ -1654,6 +1764,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template <typename _Tp> optional(_Tp) -> optional<_Tp>; #endif +#undef _GLIBCXX_USE_CONSTRAINTS_FOR_OPTIONAL + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std