diff mbox series

[committed] libstdc++: Use concepts and conditional explicit in std::optional

Message ID 20240725105547.4130530-1-jwakely@redhat.com
State New
Headers show
Series [committed] libstdc++: Use concepts and conditional explicit in std::optional | expand

Commit Message

Jonathan Wakely July 25, 2024, 10:55 a.m. UTC
Tested x86_64-linux. Pushed to trunk.

-- >8 --

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.
---
 libstdc++-v3/include/std/optional | 130 +++++++++++++++++++++++++++---
 1 file changed, 121 insertions(+), 9 deletions(-)
diff mbox series

Patch

diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional
index 700e7047aba..2cc0221865e 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