From af15c103d5ad6a7d4043dd38a4c30628c16c6997 Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Sat, 21 Oct 2017 09:33:16 +0100 Subject: [PATCH] Docs --- docs/index.md | 644 +++++++++++++++++++++++--------------------------- optional.hpp | 145 ++++++++++-- 2 files changed, 415 insertions(+), 374 deletions(-) diff --git a/docs/index.md b/docs/index.md index 82a87f8..bc1f065 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,309 +1,84 @@ # Header file `optional.hpp` -
#define TL_OPTIONAL_11_CONSTEXPR
-
-#define TL_OPTIONAL_MSVC_2015_CONSTEXPR
-
-namespace tl
+
namespace tl
 {
-    class monostate;
+    class monostate;
     
-    namespace detail
-    {
-        template <class T>
-        using remove_cv_t = typename std::remove_cv<T>::type;
-        
-        template <class T>
-        using remove_const_t = typename std::remove_const<T>::type;
-        
-        template <class T>
-        using remove_volatile_t = typename std::remove_volatile<T>::type;
-        
-        template <class T>
-        using add_cv_t = typename std::add_cv<T>::type;
-        
-        template <class T>
-        using add_const_t = typename std::add_const<T>::type;
-        
-        template <class T>
-        using add_volatile_t = typename std::add_volatile<T>::type;
-        
-        template <class T>
-        using remove_reference_t = typename std::remove_reference<T>::type;
-        
-        template <class T>
-        using add_lvalue_reference_t = typename std::add_lvalue_reference<T>::type;
-        
-        template <class T>
-        using add_rvalue_reference_t = typename std::add_rvalue_reference<T>::type;
-        
-        template <class T>
-        using remove_pointer_t = typename std::remove_pointer<T>::type;
-        
-        template <class T>
-        using add_pointer_t = typename std::add_pointer<T>::type;
-        
-        template <class T>
-        using make_signed_t = typename std::make_signed<T>::type;
-        
-        template <class T>
-        using make_unsigned_t = typename std::make_unsigned<T>::type;
-        
-        template <class T>
-        using remove_extent_t = typename std::remove_extent<T>::type;
-        
-        template <class T>
-        using remove_all_extents_t = typename std::remove_all_extents<T>::type;
-        
-        template <std::size_t N, std::size_t A = N>
-        using aligned_storage_t = typename std::aligned_storage<N, A>::type;
-        
-        template <std::size_t N, class ... Ts>
-        using aligned_union_t = typename std::aligned_union<N, Ts...>::type;
-        
-        template <class T>
-        using decay_t = typename std::decay<T>::type;
-        
-        template <bool E, class T = void>
-        using enable_if_t = typename std::enable_if<E, T>::type;
-        
-        template <bool B, class T, class F>
-        using conditional_t = typename std::conditional<B, T, F>::type;
-        
-        template <class ... Ts>
-        using common_type_t = typename std::common_type<Ts...>::type;
-        
-        template <class T>
-        using underlying_type_t = typename std::underlying_type<T>::type;
-        
-        template <class T>
-        using result_of_t = typename std::result_of<T>::type;
-        
-        template <class>
-        struct conjunction;
-        
-        template <class B>
-        struct conjunction<B>;
-        
-        template <class B, class ... Bs>
-        struct conjunction<B, Bs...>;
-        
-        template <class>
-        struct voider;
-        
-        template <class ... Ts>
-        using void_t = typename voider<Ts...>::type;
-        
-        template <class T>
-        struct is_optional_impl;
-        
-        template <class T>
-        struct is_optional_impl<optional<T>>;
-        
-        template <class T>
-        using is_optional = is_optional_impl<decay_t<T>>;
-        
-        template <typename Fn, typename ... Args, typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>{}>, int=0>
-        constexpr decltype(std::mem_fn(f)(std::forward<Args>(args)...)) invoke(Fn&& f, Args&&... args) noexcept(noexcept(std::mem_fn(f)(std::forward<Args>(args)...)));
-        
-        template <typename Fn, typename ... Args, typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>{}>>
-        constexpr decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) invoke(Fn&& f, Args&&... args) noexcept(noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)));
-        
-        template <class F, class ... Us>
-        struct invoke_result_impl<F, decltype(invoke(std::declval<F>(), std::declval<Us>()...), void()), Us...>;
-        
-        template <class F, class ... Us>
-        using invoke_result = invoke_result_impl<F, void, Us...>;
-        
-        template <class F, class ... Us>
-        using invoke_result_t = typename invoke_result<F, Us...>::type;
-        
-        template <class U>
-        using fixup_void = conditional_t<std::is_void<U>::value, monostate, U>;
-        
-        template <class F, class ... U>
-        struct get_invoke_optional_ret;
-        
-        template <class F, class ... U>
-        using get_invoke_ret = typename conditional_t<is_optional<F>::value, get_invoke_optional_ret<F, U...>, invoke_result<F, U...>>::type;
-        
-        template <class F, class U>
-        using get_map_return = optional<fixup_void<get_invoke_ret<F, U>>>;
-        
-        template <class F, class ... U>
-        using returns_void = std::is_void<get_invoke_ret<F, U...>>;
-        
-        template <class T>
-        using disable_if_optional = enable_if_t<!is_optional<T>::value>;
-        
-        template <class T>
-        using enable_if_optional = enable_if_t<is_optional<T>::value>;
-        
-        template <class T, class ... U>
-        using enable_if_ret_void = enable_if_t<returns_void<T&&, U...>::value>;
-        
-        template <class T, class ... U>
-        using disable_if_ret_void = enable_if_t<!returns_void<T&&, U...>::value>;
-    }
+    struct in_place_t;
     
-    struct in_place_t;
+    constexpr in_place_t{} in_place;
     
-    constexpr in_place_t{} in_place;
+    struct nullopt_t;
     
-    namespace detail
-    {
-        template <class T, class U>
-        using enable_forward_value = detail::enable_if_t<std::is_constructible<T, U&&>::value&&!std::is_same<detail::decay_t<U>, in_place_t>::value&&!std::is_same<optional<T>, detail::decay_t<U>>::value>;
-        
-        template <class T, class U, class Other>
-        using enable_from_other = detail::enable_if_t<std::is_constructible<T, Other>::value&&!std::is_constructible<T, optional<U>&>::value&&!std::is_constructible<T, optional<U>&&>::value&&!std::is_constructible<T, const optional<U>&>::value&&!std::is_constructible<T, const optional<U>&&>::value&&!std::is_convertible<optional<U>&, T>::value&&!std::is_convertible<optional<U>&&, T>::value&&!std::is_convertible<const optional<U>&, T>::value&&!std::is_convertible<const optional<U>&&, T>::value>;
-        
-        template <class T, class U>
-        using enable_assign_forward = detail::enable_if_t<!std::is_same<optional<T>, detail::decay_t<U>>::value&&!detail::conjunction<std::is_scalar<T>, std::is_same<T, detail::decay_t<U>>>::value&&std::is_constructible<T, U>::value&&std::is_assignable<T&, U>::value>;
-        
-        template <class T, class U, class Other>
-        using enable_assign_from_other = detail::enable_if_t<std::is_constructible<T, Other>::value&&std::is_assignable<T&, Other>::value&&!std::is_constructible<T, optional<U>&>::value&&!std::is_constructible<T, optional<U>&&>::value&&!std::is_constructible<T, const optional<U>&>::value&&!std::is_constructible<T, const optional<U>&&>::value&&!std::is_convertible<optional<U>&, T>::value&&!std::is_convertible<optional<U>&&, T>::value&&!std::is_convertible<const optional<U>&, T>::value&&!std::is_convertible<const optional<U>&&, T>::value&&!std::is_assignable<T&, optional<U>&>::value&&!std::is_assignable<T&, optional<U>&&>::value&&!std::is_assignable<T&, const optional<U>&>::value&&!std::is_assignable<T&, const optional<U>&&>::value>;
-        
-        namespace swap_adl_tests
-        {
-            struct tag;
-            
-            template <class T>
-            tag swap(T&, T&);
-            
-            template <class T, std::size_t N>
-            tag swap(T(&)[N] a, T(&)[N] b);
-            
-            template <class, class>
-            std::false_type can_swap(...) noexcept(false);
-            
-            template <class T, class U, class = decltype(swap(std::declval<T&>(), std::declval<U&>()))>
-            )>std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T&>(), std::declval<U&>())));
-            
-            template <class, class>
-            std::false_type uses_std(...);
-            
-            template <class T, class U>
-            std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag> uses_std(int);
-            
-            template <class T>
-            struct is_std_swap_noexcept;
-            
-            template <class T, std::size_t N>
-            struct is_std_swap_noexcept<T[N]>;
-            
-            template <class T, class U>
-            struct is_adl_swap_noexcept;
-        }
-        
-        template <class T, class U = T>
-        struct is_swappable;
-        
-        template <class T, std::size_t N>
-        struct is_swappable<T[N], T[N]>;
-        
-        template <class T, class U = T>
-        struct is_nothrow_swappable;
-    }
-    
-    struct nullopt_t;
-    
-    constexpr nullopt_t{nullopt_t::do_not_use{}, nullopt_t::do_not_use{}} nullopt;
+    static constexpr nullopt_t nullopt;
     
     class bad_optional_access;
     
     template <class T, class U>
-    constexpr bool operator==(const optional<T>& lhs, const optional<U>& rhs);
-    
+    constexpr bool operator==(const optional<T>& lhs, const optional<U>& rhs);
     template <class T, class U>
-    constexpr bool operator!=(const optional<T>& lhs, const optional<U>& rhs);
-    
+    constexpr bool operator!=(const optional<T>& lhs, const optional<U>& rhs);
     template <class T, class U>
-    constexpr bool operator<(const optional<T>& lhs, const optional<U>& rhs);
-    
+    constexpr bool operator<(const optional<T>& lhs, const optional<U>& rhs);
     template <class T, class U>
-    constexpr bool operator>(const optional<T>& lhs, const optional<U>& rhs);
-    
+    constexpr bool operator>(const optional<T>& lhs, const optional<U>& rhs);
     template <class T, class U>
-    constexpr bool operator<=(const optional<T>& lhs, const optional<U>& rhs);
-    
+    constexpr bool operator<=(const optional<T>& lhs, const optional<U>& rhs);
     template <class T, class U>
-    constexpr bool operator>=(const optional<T>& lhs, const optional<U>& rhs);
+    constexpr bool operator>=(const optional<T>& lhs, const optional<U>& rhs);
     
     template <class T>
-    constexpr bool operator==(const optional<T>& lhs, nullopt_t) noexcept;
+    constexpr bool operator==(const optional<T>& lhs, nullopt_t) noexcept;
+    template <class T>
+    constexpr bool operator==(nullopt_t, const optional<T>& rhs) noexcept;
+    template <class T>
+    constexpr bool operator!=(const optional<T>& lhs, nullopt_t) noexcept;
+    template <class T>
+    constexpr bool operator!=(nullopt_t, const optional<T>& rhs) noexcept;
+    template <class T>
+    constexpr bool operator<(const optional<T>&, nullopt_t) noexcept;
+    template <class T>
+    constexpr bool operator<(nullopt_t, const optional<T>& rhs) noexcept;
+    template <class T>
+    constexpr bool operator<=(const optional<T>& lhs, nullopt_t) noexcept;
+    template <class T>
+    constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept;
+    template <class T>
+    constexpr bool operator>(const optional<T>& lhs, nullopt_t) noexcept;
+    template <class T>
+    constexpr bool operator>(nullopt_t, const optional<T>&) noexcept;
+    template <class T>
+    constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept;
+    template <class T>
+    constexpr bool operator>=(nullopt_t, const optional<T>& rhs) noexcept;
+    
+    template <class T, class U>
+    constexpr bool operator==(const optional<T>& lhs, const U& rhs);
+    template <class T, class U>
+    constexpr bool operator==(const U& lhs, const optional<T>& rhs);
+    template <class T, class U>
+    constexpr bool operator!=(const optional<T>& lhs, const U& rhs);
+    template <class T, class U>
+    constexpr bool operator!=(const U& lhs, const optional<T>& rhs);
+    template <class T, class U>
+    constexpr bool operator<(const optional<T>& lhs, const U& rhs);
+    template <class T, class U>
+    constexpr bool operator<(const U& lhs, const optional<T>& rhs);
+    template <class T, class U>
+    constexpr bool operator<=(const optional<T>& lhs, const U& rhs);
+    template <class T, class U>
+    constexpr bool operator<=(const U& lhs, const optional<T>& rhs);
+    template <class T, class U>
+    constexpr bool operator>(const optional<T>& lhs, const U& rhs);
+    template <class T, class U>
+    constexpr bool operator>(const U& lhs, const optional<T>& rhs);
+    template <class T, class U>
+    constexpr bool operator>=(const optional<T>& lhs, const U& rhs);
+    template <class T, class U>
+    constexpr bool operator>=(const U& lhs, const optional<T>& rhs);
     
     template <class T>
-    constexpr bool operator==(nullopt_t, const optional<T>& rhs) noexcept;
-    
-    template <class T>
-    constexpr bool operator!=(const optional<T>& lhs, nullopt_t) noexcept;
-    
-    template <class T>
-    constexpr bool operator!=(nullopt_t, const optional<T>& rhs) noexcept;
-    
-    template <class T>
-    constexpr bool operator<(const optional<T>&, nullopt_t) noexcept;
-    
-    template <class T>
-    constexpr bool operator<(nullopt_t, const optional<T>& rhs) noexcept;
-    
-    template <class T>
-    constexpr bool operator<=(const optional<T>& lhs, nullopt_t) noexcept;
-    
-    template <class T>
-    constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept;
-    
-    template <class T>
-    constexpr bool operator>(const optional<T>& lhs, nullopt_t) noexcept;
-    
-    template <class T>
-    constexpr bool operator>(nullopt_t, const optional<T>&) noexcept;
-    
-    template <class T>
-    constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept;
-    
-    template <class T>
-    constexpr bool operator>=(nullopt_t, const optional<T>& rhs) noexcept;
-    
-    template <class T, class U>
-    constexpr bool operator==(const optional<T>& lhs, const U& rhs);
-    
-    template <class T, class U>
-    constexpr bool operator==(const U& lhs, const optional<T>& rhs);
-    
-    template <class T, class U>
-    constexpr bool operator!=(const optional<T>& lhs, const U& rhs);
-    
-    template <class T, class U>
-    constexpr bool operator!=(const U& lhs, const optional<T>& rhs);
-    
-    template <class T, class U>
-    constexpr bool operator<(const optional<T>& lhs, const U& rhs);
-    
-    template <class T, class U>
-    constexpr bool operator<(const U& lhs, const optional<T>& rhs);
-    
-    template <class T, class U>
-    constexpr bool operator<=(const optional<T>& lhs, const U& rhs);
-    
-    template <class T, class U>
-    constexpr bool operator<=(const U& lhs, const optional<T>& rhs);
-    
-    template <class T, class U>
-    constexpr bool operator>(const optional<T>& lhs, const U& rhs);
-    
-    template <class T, class U>
-    constexpr bool operator>(const U& lhs, const optional<T>& rhs);
-    
-    template <class T, class U>
-    constexpr bool operator>=(const optional<T>& lhs, const U& rhs);
-    
-    template <class T, class U>
-    constexpr bool operator>=(const U& lhs, const optional<T>& rhs);
-    
-    template <class T, detail::enable_if_t<std::is_move_constructible<T>::value>*=nullptr, detail::enable_if_t<detail::is_swappable<T>::value>*=nullptr>
-    void swap(optional<T>& lhs, optional<T>& rhs) noexcept(noexcept(lhs.swap(rhs)));
+    void swap(optional<T> &lhs, optional<T> &rhs);
     
     template <class T>
     constexpr optional<detail::decay_t<T>> make_optional(T&& v);
@@ -321,20 +96,163 @@ namespace std
 
 namespace tl
 {
-    namespace detail
-    {
-        template <class T, bool=::std::is_trivially_destructible<T>::value>
-        struct optional_storage_base;
-        
-        template <class T>
-        struct optional_storage_base<T, true>;
-    }
-    
     template <class T>
     class optional;
 }
------ +## Class `tl::monostate` + +
class monostate
+{
+};
+ +Represents an optional with no data; essentially a bool + +## Struct `tl::in_place_t` + +
struct in_place_t
+{
+    in_place_t() = default;
+};
+ +A tag type to tell optional to construct its value in-place + +## Variable `tl::in_place` + +
constexpr in_place_t{} in_place;
+ +A tag to tell optional to construct its value in-place + +## Struct `tl::nullopt_t` + +
struct nullopt_t
+{
+    struct do_not_use;
+    
+    constexpr nullopt_t(do_not_use, do_not_use) noexcept;
+};
+ +A tag type to represent an empty optional + +## Variable `tl::nullopt` + +
static constexpr nullopt_t nullopt;
+ +Represents an empty optional + +*Examples*: + + tl::optional a = tl::nullopt; + void foo (tl::optional); + foo(tl::nullopt); //pass an empty optional + +## Comparison operator `tl::operator==` + +
(1)  template <class T, class U>
+     constexpr bool operator==(const optional<T>& lhs, const optional<U>& rhs);
+
+(2)  template <class T, class U>
+     constexpr bool operator!=(const optional<T>& lhs, const optional<U>& rhs);
+
+(3)  template <class T, class U>
+     constexpr bool operator<(const optional<T>& lhs, const optional<U>& rhs);
+
+(4)  template <class T, class U>
+     constexpr bool operator>(const optional<T>& lhs, const optional<U>& rhs);
+
+(5)  template <class T, class U>
+     constexpr bool operator<=(const optional<T>& lhs, const optional<U>& rhs);
+
+(6)  template <class T, class U>
+     constexpr bool operator>=(const optional<T>& lhs, const optional<U>& rhs);
+ +Compares two optional objects + +If both optionals contain a value, they are compared with `T`s relational operators. Otherwise `lhs` and `rhs` are equal only if they are both empty, and `lhs` is less than `rhs` only if `rhs` is empty and `lhs` is not. + +## Comparison operator `tl::operator==` + +
(1)  template <class T>
+     constexpr bool operator==(const optional<T>& lhs, nullopt_t) noexcept;
+
+(2)  template <class T>
+     constexpr bool operator==(nullopt_t, const optional<T>& rhs) noexcept;
+
+(3)  template <class T>
+     constexpr bool operator!=(const optional<T>& lhs, nullopt_t) noexcept;
+
+(4)  template <class T>
+     constexpr bool operator!=(nullopt_t, const optional<T>& rhs) noexcept;
+
+(5)  template <class T>
+     constexpr bool operator<(const optional<T>&, nullopt_t) noexcept;
+
+(6)  template <class T>
+     constexpr bool operator<(nullopt_t, const optional<T>& rhs) noexcept;
+
+(7)  template <class T>
+     constexpr bool operator<=(const optional<T>& lhs, nullopt_t) noexcept;
+
+(8)  template <class T>
+     constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept;
+
+(9)  template <class T>
+     constexpr bool operator>(const optional<T>& lhs, nullopt_t) noexcept;
+
+(10)  template <class T>
+     constexpr bool operator>(nullopt_t, const optional<T>&) noexcept;
+
+(11)  template <class T>
+     constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept;
+
+(12)  template <class T>
+     constexpr bool operator>=(nullopt_t, const optional<T>& rhs) noexcept;
+ +Compares an optional to a `nullopt` + +Equivalent to comparing the optional to an empty optional + +## Comparison operator `tl::operator==` + +
(1)  template <class T, class U>
+     constexpr bool operator==(const optional<T>& lhs, const U& rhs);
+
+(2)  template <class T, class U>
+     constexpr bool operator==(const U& lhs, const optional<T>& rhs);
+
+(3)  template <class T, class U>
+     constexpr bool operator!=(const optional<T>& lhs, const U& rhs);
+
+(4)  template <class T, class U>
+     constexpr bool operator!=(const U& lhs, const optional<T>& rhs);
+
+(5)  template <class T, class U>
+     constexpr bool operator<(const optional<T>& lhs, const U& rhs);
+
+(6)  template <class T, class U>
+     constexpr bool operator<(const U& lhs, const optional<T>& rhs);
+
+(7)  template <class T, class U>
+     constexpr bool operator<=(const optional<T>& lhs, const U& rhs);
+
+(8)  template <class T, class U>
+     constexpr bool operator<=(const U& lhs, const optional<T>& rhs);
+
+(9)  template <class T, class U>
+     constexpr bool operator>(const optional<T>& lhs, const U& rhs);
+
+(10)  template <class T, class U>
+     constexpr bool operator>(const U& lhs, const optional<T>& rhs);
+
+(11)  template <class T, class U>
+     constexpr bool operator>=(const optional<T>& lhs, const U& rhs);
+
+(12)  template <class T, class U>
+     constexpr bool operator>=(const U& lhs, const optional<T>& rhs);
+ +Compares the optional with a value. + +If the optional has a value, it is compared with the other value using `T`s relational operators. Otherwise, the optional is considered less than the value. ----- @@ -346,60 +264,60 @@ class optional public: using value_type = T; - constexpr optional() noexcept = default; + constexpr optional() noexcept = default; - constexpr optional(nullopt_t) noexcept; + constexpr optional(nullopt_t) noexcept; - constexpr optional(const optional& rhs); + constexpr optional(const optional& rhs); - constexpr optional(optional&& rhs) noexcept(std::is_nothrow_move_constructible<T>::value); + constexpr optional(optional&& rhs) noexcept(std::is_nothrow_move_constructible<T>::value); template <class ... Args> - constexpr optional(detail::enable_if_t<std::is_constructible<T, Args...>::value, in_place_t>, Args&&... args); + constexpr optional('hidden', Args&&... args); template <class U, class ... Args> - constexpr optional(detail::enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value, in_place_t>, std::initializer_list<U> il, Args&&... args); + constexpr optional('hidden', std::initializer_list<U> il, Args&&... args); - template <class U = T, detail::enable_if_t<std::is_convertible<U&&, T>::value>*=nullptr, detail::enable_forward_value<T, U>*=nullptr> + template <class U = T, 'hidden', 'hidden'> constexpr optional(U&& u); - template <class U = T, detail::enable_if_t<!std::is_convertible<U&&, T>::value>*=nullptr, detail::enable_forward_value<T, U>*=nullptr> + template <class U = T, 'hidden', 'hidden'> constexpr optional(U&& u); - template <class U, detail::enable_from_other<T, U, const U&>*=nullptr, detail::enable_if_t<std::is_convertible<const U&, T>::value>*=nullptr> + template <class U, 'hidden', 'hidden'> optional(const optional<U>& rhs); - template <class U, detail::enable_from_other<T, U, const U&>*=nullptr, detail::enable_if_t<!std::is_convertible<const U&, T>::value>*=nullptr> + template <class U, 'hidden', 'hidden'> optional(const optional<U>& rhs); - template <class U, detail::enable_from_other<T, U, U&&>*=nullptr, detail::enable_if_t<std::is_convertible<U&&, T>::value>*=nullptr> + template <class U, 'hidden', 'hidden'> optional(optional<U>&& rhs); - template <class U, detail::enable_from_other<T, U, U&&>*=nullptr, detail::enable_if_t<!std::is_convertible<U&&, T>::value>*=nullptr> + template <class U, 'hidden', 'hidden'> optional(optional<U>&& rhs); ~optional() = default; - optional& operator=(nullopt_t) noexcept; + optional& operator=(nullopt_t) noexcept; optional& operator=(const optional& rhs); optional& operator=(optional&& rhs) noexcept(std::is_nothrow_move_assignable<T>::value&&std::is_nothrow_move_constructible<T>::value); - template <class U = T, detail::enable_assign_forward<T, U>*=nullptr> + template <class U = T, 'hidden'> optional& operator=(U&& u); - template <class U, detail::enable_assign_from_other<T, U, const U&>*=nullptr> + template <class U, 'hidden'> optional& operator=(const optional<U>& rhs); - template <class U, detail::enable_assign_from_other<T, U, U>*=nullptr> + template <class U, 'hidden'> optional& operator=(optional<U>&& rhs); template <class ... Args> T& emplace(Args&&... args); template <class U, class ... Args> - detail::enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value, T&> emplace(std::initializer_list<U> il, Args&&... args); + 'hidden' emplace(std::initializer_list<U> il, Args&&... args); void swap(optional& rhs) noexcept(std::is_nothrow_move_constructible<T>::value&&detail::is_nothrow_swappable<T>::value); @@ -436,73 +354,73 @@ public: void reset() noexcept; template <class F> - constexpr detail::invoke_result_t<F, T> and_then(F&& f) &; + constexpr 'hidden' and_then(F&& f) &; template <class F> - constexpr detail::invoke_result_t<F, T> and_then(F&& f) const &; + constexpr 'hidden' and_then(F&& f) const &; template <class F> - constexpr detail::invoke_result_t<F, T> and_then(F&& f) &&; + constexpr 'hidden' and_then(F&& f) &&; template <class F> - constexpr detail::invoke_result_t<F, T> and_then(F&& f) const &&; + constexpr 'hidden' and_then(F&& f) const &&; template <class F> auto map(F &&f) &; template <class F> auto map(F &&f) const &; - template <class F, detail::disable_if_optional<F>*=nullptr, detail::enable_if_ret_void<F, T&>*=nullptr> - detail::get_map_return<F, T&> map(F&& f) &; + template <class F, 'hidden', 'hidden'> + 'hidden' map(F&& f) &; - template <class F, detail::enable_if_optional<F>*=nullptr, detail::disable_if_ret_void<F, T&>*=nullptr> - detail::get_map_return<F, T&> map(F&& f) &; + template <class F, 'hidden', 'hidden'> + 'hidden' map(F&& f) &; - template <class F, detail::enable_if_optional<F>*=nullptr, detail::enable_if_ret_void<F, T&>*=nullptr> - detail::get_map_return<F, T&> map(F&& f) &; + template <class F, 'hidden', 'hidden'> + 'hidden' map(F&& f) &; template <class F> auto map(F &&f) &; template <class F> auto map(F &&f) const &&; - template <class F, detail::disable_if_optional<F>*=nullptr, detail::enable_if_ret_void<F, T&&>*=nullptr> - detail::get_map_return<F, T&&> map(F&& f) &&; + template <class F, 'hidden', 'hidden'> + 'hidden' map(F&& f) &&; - template <class F, detail::enable_if_optional<F>*=nullptr, detail::disable_if_ret_void<F, T&&>*=nullptr> - detail::get_map_return<F, T&&> map(F&& f) &&; + template <class F, 'hidden', 'hidden'> + 'hidden' map(F&& f) &&; - template <class F, detail::enable_if_optional<F>*=nullptr, detail::enable_if_ret_void<F, T&&>*=nullptr> - detail::get_map_return<F, T&&> map(F&& f) &&; + template <class F, 'hidden', 'hidden'> + 'hidden' map(F&& f) &&; - template <class F, detail::disable_if_optional<F>*=nullptr, detail::enable_if_ret_void<F, T const&>*=nullptr> - detail::get_map_return<F, T const&> map(F&& f) const &; + template <class F, 'hidden', 'hidden'> + 'hidden' map(F&& f) const &; - template <class F, detail::enable_if_optional<F>*=nullptr, detail::disable_if_ret_void<F, T const&>*=nullptr> - constexpr detail::get_map_return<F, T const&> map(F&& f) const &; + template <class F, 'hidden', 'hidden'> + constexpr 'hidden' map(F&& f) const &; - template <class F, detail::enable_if_optional<F>*=nullptr, detail::enable_if_ret_void<F, T const&>*=nullptr> - detail::get_map_return<F, T const&> map(F&& f) const &; + template <class F, 'hidden', 'hidden'> + 'hidden' map(F&& f) const &; - template <class F, detail::disable_if_optional<F>*=nullptr, detail::enable_if_ret_void<F, T const&&>*=nullptr> - detail::get_map_return<F, T const&&> map(F&& f) const &&; + template <class F, 'hidden', 'hidden'> + 'hidden' map(F&& f) const &&; - template <class F, detail::enable_if_optional<F>*=nullptr, detail::disable_if_ret_void<F, T const&&>*=nullptr> - constexpr detail::get_map_return<F, T const&&> map(F&& f) const &&; + template <class F, 'hidden', 'hidden'> + constexpr 'hidden' map(F&& f) const &&; - template <class F, detail::enable_if_optional<F>*=nullptr, detail::enable_if_ret_void<F, T const&&>*=nullptr> - detail::get_map_return<F, T&> map(F&& f) const &&; + template <class F, 'hidden', 'hidden'> + 'hidden' map(F&& f) const &&; template <class F> optional<T> or_else (F &&f) &; template <class F> optional<T> or_else (F &&f) const &; - template <class F, detail::disable_if_ret_void<F>*=nullptr> + template <class F, 'hidden'> constexpr optional<T> or_else(F&& f) &; template <class F> optional<T> or_else (F &&f) &&; template <class F> optional<T> or_else (F &&f) const &&; - template <class F, detail::disable_if_ret_void<F>*=nullptr> + template <class F, 'hidden'> constexpr optional<T> or_else(F&& f) &&; - template <class F, detail::disable_if_ret_void<F>*=nullptr> + template <class F, 'hidden'> constexpr optional<T> or_else(F&& f) const &; - template <class F, detail::disable_if_ret_void<F>*=nullptr> + template <class F, 'hidden'> optional<T> or_else(F&& f) const &&; template <class F, class U> @@ -532,13 +450,41 @@ public: An optional object is an object that contains the storage for another object and manages the lifetime of this contained object, if any. The contained object may be initialized after the optional object has been initialized, and may be destroyed before the optional object has been destroyed. The initialization state of the contained object is tracked by the optional object. +### Default constructor `tl::optional::optional` + +
constexpr optional() noexcept = default;
+ +Constructs an optional that does not contain a value. + +### Constructor `tl::optional::optional` + +
constexpr optional(nullopt_t) noexcept;
+ +Constructs an optional that does not contain a value. + +### Constructor `tl::optional::optional` + +
constexpr optional(const optional& rhs);
+ +Copy constructor + +If `rhs` contains a value, the stored value is direct-initialized with it. Otherwise, the constructed optional is empty. + +### Constructor `tl::optional::optional` + +
constexpr optional(optional&& rhs) noexcept(std::is_nothrow_move_constructible<T>::value);
+ +Move constructor + +If `rhs` contains a value, the stored value is direct-initialized with it. Otherwise, the constructed optional is empty. + ### Function template `tl::optional::and_then`
(1)  template <class F>
-     constexpr detail::invoke_result_t<F, T> and_then(F&& f) &;
+     constexpr 'hidden' and_then(F&& f) &;
 
 (2)  template <class F>
-     constexpr detail::invoke_result_t<F, T> and_then(F&& f) const &;
+ constexpr 'hidden' and_then(F&& f) const &;
Carries out some operation which returns an optional on the stored object if there is one. @@ -549,10 +495,10 @@ Carries out some operation which returns an optional on the stored object if the ### Function template `tl::optional::and_then`
(1)  template <class F>
-     constexpr detail::invoke_result_t<F, T> and_then(F&& f) &&;
+     constexpr 'hidden' and_then(F&& f) &&;
 
 (2)  template <class F>
-     constexpr detail::invoke_result_t<F, T> and_then(F&& f) const &&;
+ constexpr 'hidden' and_then(F&& f) const &&; Carries out some operation which returns an optional on the stored object if there is one. diff --git a/optional.hpp b/optional.hpp index abafbed..90389c6 100644 --- a/optional.hpp +++ b/optional.hpp @@ -18,20 +18,35 @@ #include #if __cplusplus == 201103L || _MSC_VER == 1900 +/// \exclude #define TL_OPTIONAL_11_CONSTEXPR #else +/// \exclude #define TL_OPTIONAL_11_CONSTEXPR constexpr #endif #if _MSC_VER == 1900 +/// \exclude #define TL_OPTIONAL_MSVC_2015_CONSTEXPR #else +/// \exclude #define TL_OPTIONAL_MSVC_2015_CONSTEXPR constexpr #endif namespace tl { -template class optional; +/// \brief Represents an optional with no data; essentially a bool class monostate {}; + +/// \brief A tag type to tell optional to construct its value in-place +struct in_place_t { + explicit in_place_t() = default; +}; +/// \brief A tag to tell optional to construct its value in-place +static constexpr in_place_t in_place{}; + +template class optional; + +/// \exclude namespace detail { template using remove_cv_t = typename std::remove_cv::type; template using remove_const_t = typename std::remove_const::type; @@ -150,17 +165,7 @@ using enable_if_ret_void = enable_if_t::value>; template using disable_if_ret_void = enable_if_t::value>; -} // namespace detail -struct in_place_t { - explicit in_place_t() = default; -}; -static constexpr in_place_t in_place{}; - -// [optional.optional], class template optional -template class optional; - -namespace detail { template using enable_forward_value = detail::enable_if_t::value && @@ -278,156 +283,201 @@ struct is_nothrow_swappable } // namespace detail -// [optional.nullopt], no-value state indicator +/// \brief A tag type to represent an empty optional struct nullopt_t { struct do_not_use {}; constexpr explicit nullopt_t(do_not_use, do_not_use) noexcept {} }; +/// \brief Represents an empty optional +/// \synopsis static constexpr nullopt_t nullopt; +/// +/// *Examples*: +/// ``` +/// tl::optional a = tl::nullopt; +/// void foo (tl::optional); +/// foo(tl::nullopt); //pass an empty optional +/// ``` static constexpr nullopt_t nullopt{nullopt_t::do_not_use{}, nullopt_t::do_not_use{}}; -// [optional.bad.access], class bad_optional_access class bad_optional_access : public std::exception { public: bad_optional_access() = default; const char *what() const noexcept { return "Optional has no value"; } }; -// [optional.relops], relational operators +/// \group relop +/// \brief Compares two optional objects +/// \details If both optionals contain a value, they are compared with `T`s relational operators. +/// Otherwise `lhs` and `rhs` are equal only if they are both empty, and `lhs` is less than `rhs` +/// only if `rhs` is empty and `lhs` is not. template inline constexpr bool operator==(const optional &lhs, const optional &rhs) { return lhs.has_value() == rhs.has_value() && (!lhs.has_value() || *lhs == *rhs); } +/// \group relop template inline constexpr bool operator!=(const optional &lhs, const optional &rhs) { return lhs.has_value() != rhs.has_value() || (lhs.has_value() && *lhs != *rhs); } +/// \group relop template inline constexpr bool operator<(const optional &lhs, const optional &rhs) { return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); } +/// \group relop template inline constexpr bool operator>(const optional &lhs, const optional &rhs) { return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); } +/// \group relop template inline constexpr bool operator<=(const optional &lhs, const optional &rhs) { return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); } +/// \group relop template inline constexpr bool operator>=(const optional &lhs, const optional &rhs) { return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); } -// [optional.nullops], comparison with nullopt +/// \group relop_nullopt +/// \brief Compares an optional to a `nullopt` +/// \details Equivalent to comparing the optional to an empty optional template inline constexpr bool operator==(const optional &lhs, nullopt_t) noexcept { return !lhs.has_value(); } +/// \group relop_nullopt template inline constexpr bool operator==(nullopt_t, const optional &rhs) noexcept { return !rhs.has_value(); } +/// \group relop_nullopt template inline constexpr bool operator!=(const optional &lhs, nullopt_t) noexcept { return lhs.has_value(); } +/// \group relop_nullopt template inline constexpr bool operator!=(nullopt_t, const optional &rhs) noexcept { return rhs.has_value(); } +/// \group relop_nullopt template inline constexpr bool operator<(const optional &, nullopt_t) noexcept { return false; } +/// \group relop_nullopt template inline constexpr bool operator<(nullopt_t, const optional &rhs) noexcept { return rhs.has_value(); } +/// \group relop_nullopt template inline constexpr bool operator<=(const optional &lhs, nullopt_t) noexcept { return !lhs.has_value(); } +/// \group relop_nullopt template inline constexpr bool operator<=(nullopt_t, const optional &) noexcept { return true; } +/// \group relop_nullopt template inline constexpr bool operator>(const optional &lhs, nullopt_t) noexcept { return lhs.has_value(); } +/// \group relop_nullopt template inline constexpr bool operator>(nullopt_t, const optional &) noexcept { return false; } +/// \group relop_nullopt template inline constexpr bool operator>=(const optional &, nullopt_t) noexcept { return true; } +/// \group relop_nullopt template inline constexpr bool operator>=(nullopt_t, const optional &rhs) noexcept { return !rhs.has_value(); } -// [optional.comp_with_t], comparison with T + +/// \group relop_t +/// \brief Compares the optional with a value. +/// \details If the optional has a value, it is compared with the other value using `T`s relational operators. +/// Otherwise, the optional is considered less than the value. template inline constexpr bool operator==(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs == rhs : false; } +/// \group relop_t template inline constexpr bool operator==(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs == *rhs : false; } +/// \group relop_t template inline constexpr bool operator!=(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs != rhs : true; } +/// \group relop_t template inline constexpr bool operator!=(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs != *rhs : true; } +/// \group relop_t template inline constexpr bool operator<(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs < rhs : true; } +/// \group relop_t template inline constexpr bool operator<(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs < *rhs : false; } +/// \group relop_t template inline constexpr bool operator<=(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs <= rhs : true; } +/// \group relop_t template inline constexpr bool operator<=(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs <= *rhs : false; } +/// \group relop_t template inline constexpr bool operator>(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs > rhs : false; } +/// \group relop_t template inline constexpr bool operator>(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs > *rhs : true; } +/// \group relop_t template inline constexpr bool operator>=(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs >= rhs : false; } +/// \group relop_t template inline constexpr bool operator>=(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs >= *rhs : true; } -// [optional.specalg], specialized algorithms +/// \synopsis template \nvoid swap(optional &lhs, optional &rhs); template ::value> * = nullptr, detail::enable_if_t::value> * = nullptr> @@ -451,7 +501,6 @@ inline constexpr optional make_optional(std::initializer_list il, } } // namespace tl -// [optional.hash], hash support namespace std { // TODO SFINAE template struct hash> { @@ -459,12 +508,13 @@ template struct hash> { if (!o.has_value()) return 0; - return hash>()(*o); + return std::hash>()(*o); } }; } // namespace std namespace tl { +/// \exclude namespace detail { template ::value> struct optional_storage_base { @@ -511,15 +561,24 @@ template struct optional_storage_base { }; } // namespace detail + +/// An optional object is an object that contains the storage for another object and manages the lifetime of this contained object, if any. The contained object may be initialized after the optional object has been initialized, and may be destroyed before the optional object has been destroyed. The initialization state of the contained object is tracked by the optional object. template class optional : private detail::optional_storage_base { using base = detail::optional_storage_base; public: using value_type = T; - // [optional.ctor], constructors + /// Constructs an optional that does not contain a value. constexpr optional() noexcept = default; + + /// Constructs an optional that does not contain a value. constexpr optional(nullopt_t) noexcept {}; + + /// Copy constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with it. + /// Otherwise, the constructed optional is empty. TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) { if (rhs.has_value()) { this->m_has_value = true; @@ -528,6 +587,10 @@ public: } // TODO conditionally disable + /// Move constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with it. + /// Otherwise, the constructed optional is empty. TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) noexcept( std::is_nothrow_move_constructible::value) { if (rhs.has_value()) { @@ -594,10 +657,8 @@ public: new (std::addressof(this->m_value)) T(std::move(*rhs)); } - // [optional.dtor], destructor ~optional() = default; - // [optional.assign], assignment optional &operator=(nullopt_t) noexcept { if (has_value()) { this->m_value.~T(); @@ -706,7 +767,6 @@ public: new (std::addressof(this->m_value)) T(il, std::forward(args)...); } - // [optional.swap], swap void swap(optional &rhs) noexcept(std::is_nothrow_move_constructible::value &&detail::is_nothrow_swappable::value) { @@ -724,7 +784,6 @@ public: } } - // [optional.observe], observers constexpr const T *operator->() const { return std::addressof(this->m_value); } @@ -774,7 +833,6 @@ public: return has_value() ? **this : static_cast(std::forward(u)); } - // [optional.mod], modifiers void reset() noexcept { if (has_value()) { this->m_value.~T(); @@ -782,6 +840,11 @@ public: } } + + /// \group and_then + /// Carries out some operation which returns an optional on the stored object if there is one. + /// \requires `std::invoke(std::forward(f), value())` returns a `std::optional` for some `U`. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), value())`. Returns a `std::optional`. The return value is empty if `*this` is empty, otherwise the return value of `std::invoke(std::forward(f), value())` is returned. template TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) & { using result = detail::invoke_result_t; @@ -792,6 +855,10 @@ public: : result(nullopt); } + /// \group and_then_rval + /// Carries out some operation which returns an optional on the stored object if there is one. + /// \requires `std::invoke(std::forward(f), std::move(value()))` returns a `std::optional` for some `U`. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), std::move(value()))`. Returns a `std::optional`. The return value is empty if `*this` is empty, otherwise the return value of `std::invoke(std::forward(f), std::move(value()))` is returned. template TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) && { using result = detail::invoke_result_t; @@ -802,6 +869,7 @@ public: : result(nullopt); } + /// \group and_then template constexpr detail::invoke_result_t and_then(F &&f) const & { using result = detail::invoke_result_t; @@ -812,6 +880,7 @@ public: : result(nullopt); } + /// \group and_then_rval template constexpr detail::invoke_result_t and_then(F &&f) const && { using result = detail::invoke_result_t; @@ -822,6 +891,10 @@ public: : result(nullopt); } + /// \group map + /// \synopsis template auto map(F &&f) &; + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), value())`. Returns a `std::optional`. The return value is empty if `*this` is empty, otherwise an `optional` is constructed from the return value of `std::invoke(std::forward(f), value())` and is returned. template * = nullptr, detail::disable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) & @@ -861,6 +934,10 @@ public: return monostate{}; } + /// \group map_rval + /// \synopsis template auto map(F &&f) &; + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), std::move(value()))`. Returns a `std::optional`. The return value is empty if `*this` is empty, otherwise an `optional` is constructed from the return value of `std::invoke(std::forward(f), std::move(value()))` and is returned. template * = nullptr, detail::disable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) && { @@ -898,6 +975,8 @@ public: return monostate{}; } + /// \group map + /// \synopsis template auto map(F &&f) const &; template * = nullptr, detail::disable_if_ret_void * = nullptr> constexpr detail::get_map_return map(F &&f) const & { @@ -936,6 +1015,8 @@ public: return monostate{}; } + /// \group map_rval + /// \synopsis template auto map(F &&f) const &&; template * = nullptr, detail::disable_if_ret_void * = nullptr> constexpr detail::get_map_return map(F &&f) const && { @@ -973,6 +1054,11 @@ public: return monostate{}; } + /// \group or_else + /// \brief Calls `f` if the optional is empty + /// \synopsis template optional or_else (F &&f) &; + /// \requires `std::invoke_result_t` must be void or convertible to `optional`. + /// \effects If `*this` has a value, returns `*this`. Otherwise, if `f` returns `void`, calls `std::forward(f)` and returns `std::nullopt`. Otherwise, returns `std::forward(f)()`. template * = nullptr> optional TL_OPTIONAL_MSVC_2015_CONSTEXPR or_else(F &&f) & { if (has_value()) @@ -987,6 +1073,11 @@ public: return has_value() ? *this : std::forward(f)(); } + /// \group or_else_rval + /// \brief Calls `f` if the optional is empty + /// \synopsis template optional or_else (F &&f) &&; + /// \requires `std::invoke_result_t` must be void or convertible to `optional`. + /// \effects If `*this` has a value, returns `std::move(*this)`. Otherwise, if `f` returns `void`, calls `std::forward(f)` and returns `std::nullopt`. Otherwise, returns `std::forward(f)()`. template * = nullptr> optional or_else(F &&f) && { if (has_value()) @@ -1001,6 +1092,8 @@ public: return has_value() ? std::move(*this) : std::forward(f)(); } + /// \group or_else + /// \synopsis template optional or_else (F &&f) const &; template * = nullptr> optional or_else(F &&f) const & { if (has_value()) @@ -1015,6 +1108,8 @@ public: return has_value() ? *this : std::forward(f)(); } + /// \group or_else_rval + /// \synopsis template optional or_else (F &&f) const &&; template * = nullptr> optional or_else(F &&f) const && { if (has_value())