From dcfccd46d87e52e3d4bbee97c2e1b7e2143266eb Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Sat, 21 Oct 2017 19:04:41 +0100 Subject: [PATCH] Docs --- CMakeLists.txt | 20 +- docs/index.md | 665 +++++++++++----------- optional.hpp | 1440 +++++++++++++++++++++++++----------------------- 3 files changed, 1096 insertions(+), 1029 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 55227bc..6add6fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,9 @@ cmake_minimum_required(VERSION 3.0) -project(cmake_test) +project(optional) # Prepare "Catch" library for other executables -set(CATCH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(CATCH_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/test) add_library(Catch INTERFACE) target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR}) @@ -19,5 +19,19 @@ set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/tests/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/nullopt.cpp) add_executable(tests ${TEST_SOURCES}) -target_link_libraries(tests Catch) + +add_library(optional INTERFACE) +target_sources(optional INTERFACE ${CMAKE_SOURCE_DIR}/optional.hpp) +target_include_directories(optional INTERFACE ${CMAKE_SOURCE_DIR}) + +target_link_libraries(tests Catch optional) set_property(TARGET tests PROPERTY CXX_STANDARD 14) + + +find_package(standardese REQUIRED) # find standardese after installation + +# generates a custom target that will run standardese to generate the documentation +standardese_generate(optional + INCLUDE_DIRECTORY . + CONFIG ${CMAKE_SOURCE_DIR}/standardese.config + INPUT optional.hpp) diff --git a/docs/index.md b/docs/index.md index bf6bd28..3aed36f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -14,6 +14,9 @@ class bad_optional_access; + template <class T> + class optional; + template <class T, class U> constexpr bool operator==(const optional<T>& lhs, const optional<U>& rhs); template <class T, class U> @@ -92,12 +95,6 @@ namespace std { -} - -namespace tl -{ - template <class T> - class optional; } ## Class `tl::monostate` @@ -106,7 +103,7 @@ namespace tl { }; -Represents an optional with no data; essentially a bool +Used to represent an optional with no data; essentially a bool ## Struct `tl::in_place_t` @@ -146,6 +143,318 @@ Represents an empty optional void foo (tl::optional); foo(tl::nullopt); //pass an empty optional +## Class template `tl::optional` + +
template <class T>
+class optional
+{
+public:
+    template <class F> constexpr auto and_then(F &&f);
+    template <class F> constexpr auto and_then(F &&f) const;
+    
+    template <class F> auto map(F &&f);
+    template <class F> auto 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, class U> U map_or(F &&f, U &&u);
+    template <class F, class U> U map_or(F &&f, U &&u) const;
+    
+    template <class F, class U> U map_or_else(F &&f, U &&u);
+    template <class F, class U> U map_or_else(F &&f, U &&u) const;
+    
+    using value_type = T;
+    
+    constexpr optional() noexcept = default;
+    constexpr optional(nullopt_t) noexcept;
+    
+    constexpr optional(const optional& rhs);
+    
+    constexpr optional(optional&& rhs) noexcept(std::is_nothrow_move_constructible<T>::value);
+    
+    template <class... Args> constexpr explicit optional(in_place_t, Args&&... args);
+    template <class U, class... Args>
+    constexpr explicit optional(in_place_t, std::initializer_list<U>&, Args&&... args);
+    
+    template <class U=T> constexpr optional(U &&u);
+    
+    template <class U> optional(const optional<U> &rhs);
+    
+    template <class U> optional(optional<U> &&rhs);
+    
+    ~optional() = default;
+    
+    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);
+    
+    optional &operator=(U &&u);
+    
+    optional &operator=(const optional<U> & rhs);
+    
+    optional &operator=(optional<U> && rhs);
+    
+    template <class ... Args>
+    T& emplace(Args&&... args);
+    template <class U, class... Args>
+    T& 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);
+    
+    constexpr const T *operator->() const;
+    constexpr T *operator->();
+    
+    constexpr T &operator*();
+    constexpr const T &operator*() const;
+    
+    constexpr bool has_value() const noexcept;
+    constexpr operator bool() const noexcept;
+    
+    constexpr T &value();
+    constexpr const T &value() const;
+    
+    template <class U>
+    constexpr T value_or(U&& u) const &;
+    template <class U>
+    constexpr T value_or(U&& u) &&;
+    
+    void reset() noexcept;
+};
+ +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. + +### Function template `tl::optional::and_then` + +
(1)  template <class F> constexpr auto and_then(F &&f);
+
+(2)  template <class F> constexpr auto and_then(F &&f) const;
+ +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. + +### Function template `tl::optional::map` + +
(1)  template <class F> auto map(F &&f);
+
+(2)  template <class F> auto map(F &&f) const;
+ +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. + +### Function template `tl::optional::or_else` + +
(1)  template <class F> optional<T> or_else (F &&f);
+
+(2)  template <class F> optional<T> or_else (F &&f) const;
+ +Calls `f` if the optional is empty + +*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)()`. + +### Function template `tl::optional::map_or` + +
(1)  template <class F, class U> U map_or(F &&f, U &&u);
+
+(2)  template <class F, class U> U map_or(F &&f, U &&u) const;
+ +Maps the stored value with `f` if there is one, otherwise returns `u` + +If there is a value stored, then `f` is called with `**this` and the value is returned. Otherwise `u` is returned. + +### Function template `tl::optional::map_or_else` + +
(1)  template <class F, class U> U map_or_else(F &&f, U &&u);
+
+(2)  template <class F, class U> U map_or_else(F &&f, U &&u) const;
+ +Maps the stored value with `f` if there is one, otherwise calls `u` and returns the result. + +If there is a value stored, then `f` is called with `**this` and the value is returned. Otherwise `std::forward(u)()` is returned. + +### Default constructor `tl::optional::optional` + +
(1)  constexpr optional() noexcept = default;
+
+(2)  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::optional` + +
(1)  template <class... Args> constexpr explicit optional(in_place_t, Args&&... args);
+
+(2)  template <class U, class... Args>
+     constexpr explicit optional(in_place_t, std::initializer_list<U>&, Args&&... args);
+ +Constructs the stored value in-place using the given arguments. + +### Function template `tl::optional::optional` + +
template <class U=T> constexpr optional(U &&u);
+ +Constructs the stored value with `u`. + +### Function template `tl::optional::optional` + +
template <class U> optional(const optional<U> &rhs);
+ +Converting copy constructor. + +### Function template `tl::optional::optional` + +
template <class U> optional(optional<U> &&rhs);
+ +Converting move constructor. + +### Destructor `tl::optional::~optional` + +
~optional() = default;
+ +Destructor. + +### Assignment operator `tl::optional::operator=` + +
optional& operator=(nullopt_t) noexcept;
+ +Assignment to empty. + +Destroys the current value if there is one. + +### Assignment operator `tl::optional::operator=` + +
optional& operator=(const optional& rhs);
+ +Copy assignment. + +Copies the value from `rhs` if there is one. Otherwise resets the stored value in `*this`. + +### Assignment operator `tl::optional::operator=` + +
optional& operator=(optional&& rhs) noexcept(std::is_nothrow_move_assignable<T>::value&&std::is_nothrow_move_constructible<T>::value);
+ +Move assignment. + +Moves the value from `rhs` if there is one. Otherwise resets the stored value in `*this`. + +### Assignment operator `tl::optional::operator=` + +
optional &operator=(U &&u);
+ +Assigns the stored value from `u`, destroying the old value if there was one. + +### Assignment operator `tl::optional::operator=` + +
optional &operator=(const optional<U> & rhs);
+ +Converting copy assignment operator. + +Copies the value from `rhs` if there is one. Otherwise resets the stored value in `*this`. + +### Assignment operator `tl::optional::operator=` + +
optional &operator=(optional<U> && rhs);
+ +Converting move assignment operator. + +Moves the value from `rhs` if there is one. Otherwise resets the stored value in `*this`. + +### Function template `tl::optional::emplace` + +
(1)  template <class ... Args>
+     T& emplace(Args&&... args);
+
+(2)  template <class U, class... Args>
+     T& emplace(std::initializer_list<U> il, Args &&... args);
+ +Constructs the value in-place, destroying the current one if there is one. + +### Function `tl::optional::swap` + +
void swap(optional& rhs) noexcept(std::is_nothrow_move_constructible<T>::value&&detail::is_nothrow_swappable<T>::value);
+ +Swaps this optional with the other. + +If neither optionals have a value, nothing happens. If both have a value, the values are swapped. If one has a value, it is moved to the other and the movee is left valueless. + +### Operator `tl::optional::operator->` + +
(1)  constexpr const T *operator->() const;
+
+(2)  constexpr T *operator->();
+ +*Returns*: a pointer to the stored value + +*Requires*: a value is stored + +### Operator `tl::optional::operator*` + +
(1)  constexpr T &operator*();
+
+(2)  constexpr const T &operator*() const;
+ +*Returns*: the stored value + +*Requires*: a value is stored + +### Function `tl::optional::has_value` + +
(1)  constexpr bool has_value() const noexcept;
+
+(2)  constexpr operator bool() const noexcept;
+ +*Returns*: whether or not the optional has a value + +### Function `tl::optional::value` + +
(1)  constexpr T &value();
+
+(2)  constexpr const T &value() const;
+ +*Returns*: the contained value if there is one, otherwise throws \[bad\_optional\_access\] + +### Function template `tl::optional::value_or` + +
(1)  template <class U>
+     constexpr T value_or(U&& u) const &;
+
+(2)  template <class U>
+     constexpr T value_or(U&& u) &&;
+ +*Returns*: the stored value if there is one, otherwise returns `u` + +### Function `tl::optional::reset` + +
void reset() noexcept;
+ +Destroys the stored value if one exists, making the optional empty + +----- + ## Comparison operator `tl::operator==`
(1)  template <class T, class U>
@@ -255,345 +564,3 @@ 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.
 
 -----
-
-## Class template `tl::optional`
-
-
template <class T>
-class optional
-{
-public:
-    using value_type = T;
-    
-    constexpr optional() noexcept = default;
-    
-    constexpr optional(nullopt_t) noexcept;
-    
-    constexpr optional(const optional& rhs);
-    
-    constexpr optional(optional&& rhs) noexcept(std::is_nothrow_move_constructible<T>::value);
-    
-    template <class ... Args>
-    constexpr optional('hidden', Args&&... args);
-    
-    template <class U, class ... Args>
-    constexpr optional('hidden', std::initializer_list<U> il, Args&&... args);
-    
-    template <class U = T, 'hidden', 'hidden'>
-    constexpr optional(U&& u);
-    
-    template <class U = T, 'hidden', 'hidden'>
-    constexpr optional(U&& u);
-    
-    template <class U, 'hidden', 'hidden'>
-    optional(const optional<U>& rhs);
-    
-    template <class U, 'hidden', 'hidden'>
-    optional(const optional<U>& rhs);
-    
-    template <class U, 'hidden', 'hidden'>
-    optional(optional<U>&& rhs);
-    
-    template <class U, 'hidden', 'hidden'>
-    optional(optional<U>&& rhs);
-    
-    ~optional() = default;
-    
-    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, 'hidden'>
-    optional& operator=(U&& u);
-    
-    template <class U, 'hidden'>
-    optional& operator=(const optional<U>& rhs);
-    
-    template <class U, 'hidden'>
-    optional& operator=(optional<U>&& rhs);
-    
-    template <class ... Args>
-    T& emplace(Args&&... args);
-    
-    template <class U, class ... 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);
-    
-    constexpr const T* operator->() const;
-    
-    constexpr T* operator->();
-    
-    constexpr const T& operator*() const &;
-    
-    constexpr T& operator*() &;
-    
-    constexpr T&& operator*() &&;
-    
-    constexpr const T&& operator*() const &&;
-    
-    constexpr operator bool() const noexcept;
-    
-    constexpr bool has_value() const noexcept;
-    
-    constexpr const T& value() const &;
-    
-    constexpr T& value() &;
-    
-    constexpr T&& value() &&;
-    
-    constexpr const T&& value() const &&;
-    
-    template <class U>
-    constexpr T value_or(U&& u) const &;
-    
-    template <class U>
-    constexpr T value_or(U&& u) &&;
-    
-    void reset() noexcept;
-    
-    template <class F>
-    constexpr 'hidden' and_then(F&& f) &;
-    template <class F>
-    constexpr 'hidden' and_then(F&& f) const &;
-    
-    template <class F>
-    constexpr 'hidden' and_then(F&& f) &&;
-    template <class F>
-    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, 'hidden', 'hidden'>
-    'hidden' map(F&& f) &;
-    
-    template <class F, 'hidden', 'hidden'>
-    'hidden' 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, 'hidden', 'hidden'>
-    'hidden' map(F&& f) &&;
-    
-    template <class F, 'hidden', 'hidden'>
-    'hidden' map(F&& f) &&;
-    
-    template <class F, 'hidden', 'hidden'>
-    'hidden' map(F&& f) &&;
-    
-    template <class F, 'hidden', 'hidden'>
-    'hidden' map(F&& f) const &;
-    
-    template <class F, 'hidden', 'hidden'>
-    constexpr 'hidden' map(F&& f) const &;
-    
-    template <class F, 'hidden', 'hidden'>
-    'hidden' map(F&& f) const &;
-    
-    template <class F, 'hidden', 'hidden'>
-    'hidden' map(F&& f) const &&;
-    
-    template <class F, 'hidden', 'hidden'>
-    constexpr 'hidden' 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, '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, 'hidden'>
-    constexpr optional<T> or_else(F&& f) &&;
-    
-    template <class F, 'hidden'>
-    constexpr optional<T> or_else(F&& f) const &;
-    
-    template <class F, 'hidden'>
-    optional<T> or_else(F&& f) const &&;
-    
-    template <class F, class U>
-    U map_or(F&& f, U&& u) &;
-    template <class F, class U>
-    U map_or(F&& f, U&& u) const &;
-    
-    template <class F, class U>
-    U map_or(F&& f, U&& u) &&;
-    template <class F, class U>
-    U map_or(F&& f, U&& u) const &&;
-    
-    template <class F, class U>
-    U map_or_else(F&& f, U&& u) &;
-    template <class F, class U>
-    U map_or_else(F&& f, U&& u) const &;
-    
-    template <class F, class U>
-    U map_or_else(F&& f, U&& u) &&;
-    template <class F, class U>
-    U map_or_else(F&& f, U&& u) const &&;
-};
- -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 'hidden' and_then(F&& f) &;
-
-(2)  template <class F>
-     constexpr 'hidden' and_then(F&& f) const &;
- -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. - -### Function template `tl::optional::and_then` - -
(1)  template <class F>
-     constexpr 'hidden' and_then(F&& f) &&;
-
-(2)  template <class F>
-     constexpr 'hidden' and_then(F&& f) const &&;
- -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. - -### Function template `tl::optional::map` - -
(1)  template <class F> auto map(F &&f) &;
-
-(2)  template <class F> auto map(F &&f) const &;
- -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. - -### Function template `tl::optional::map` - -
(1)  template <class F> auto map(F &&f) &;
-
-(2)  template <class F> auto map(F &&f) const &&;
- -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. - -### Function template `tl::optional::or_else` - -
(1)  template <class F> optional<T> or_else (F &&f) &;
-
-(2)  template <class F> optional<T> or_else (F &&f) const &;
- -Calls `f` if the optional is empty - -*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)()`. - -### Function template `tl::optional::or_else` - -
(1)  template <class F> optional<T> or_else (F &&f) &&;
-
-(2)  template <class F> optional<T> or_else (F &&f) const &&;
- -Calls `f` if the optional is empty - -*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)()`. - -### Function template `tl::optional::map_or` - -
(1)  template <class F, class U>
-     U map_or(F&& f, U&& u) &;
-
-(2)  template <class F, class U>
-     U map_or(F&& f, U&& u) const &;
- -Maps the stored value with `f` if there is one, otherwise returns `u` - -If there is a value stored, then `f` is called with `**this` and the value is returned. Otherwise `u` is returned. - -### Function template `tl::optional::map_or` - -
(1)  template <class F, class U>
-     U map_or(F&& f, U&& u) &&;
-
-(2)  template <class F, class U>
-     U map_or(F&& f, U&& u) const &&;
- -Maps the stored value with `f` if there is one, otherwise returns `u` - -If there is a value stored, then `f` is called with `std::move(**this)` and the value is returned. Otherwise `u` is returned. - -### Function template `tl::optional::map_or_else` - -
(1)  template <class F, class U>
-     U map_or_else(F&& f, U&& u) &;
-
-(2)  template <class F, class U>
-     U map_or_else(F&& f, U&& u) const &;
- -Maps the stored value with `f` if there is one, otherwise calls `u` and returns the result. - -If there is a value stored, then `f` is called with `**this` and the value is returned. Otherwise `std::forward(u)()` is returned. - -### Function template `tl::optional::map_or_else` - -
(1)  template <class F, class U>
-     U map_or_else(F&& f, U&& u) &&;
-
-(2)  template <class F, class U>
-     U map_or_else(F&& f, U&& u) const &&;
- -Maps the stored value with `f` if there is one, otherwise calls `u` and returns the result. - -If there is a value stored, then `f` is called with `std::move(**this)` and the value is returned. Otherwise `std::forward(u)()` is returned. - ------ - ------ diff --git a/optional.hpp b/optional.hpp index 2d8e328..623e10d 100644 --- a/optional.hpp +++ b/optional.hpp @@ -34,7 +34,7 @@ #endif namespace tl { -/// \brief Represents an optional with no data; essentially a bool +/// \brief Used to represent an optional with no data; essentially a bool class monostate {}; /// \brief A tag type to tell optional to construct its value in-place @@ -281,6 +281,50 @@ struct is_nothrow_swappable }; #endif + +template ::value> +struct optional_storage_base { + TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) {} + + template + TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base(in_place_t, U &&... u) noexcept + : m_value(std::forward(u)...), m_has_value(true) {} + + ~optional_storage_base() { + if (m_has_value) { + m_value.~T(); + m_has_value = false; + } + } + + struct dummy {}; + union { + dummy m_dummy; + T m_value; + }; + + bool m_has_value; +}; + +template struct optional_storage_base { + TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base() noexcept + : m_dummy(), m_has_value(false) {} + + template + TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base(in_place_t, + U &&... u) noexcept + : m_value(std::forward(u)...), m_has_value(true) {} + + ~optional_storage_base() = default; + + struct dummy {}; + union { + dummy m_dummy; + T m_value; + }; + + bool m_has_value = false; +}; } // namespace detail /// \brief A tag type to represent an empty optional @@ -306,6 +350,722 @@ public: const char *what() const noexcept { return "Optional has no value"; } }; +/// 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: + + /// \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. + /// \group and_then + /// \synopsis template constexpr auto and_then(F &&f); + template + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + /// \exclude + template + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : result(nullopt); + } + + /// \group and_then + /// \synopsis template constexpr auto and_then(F &&f) const; + template + constexpr detail::invoke_result_t and_then(F &&f) const & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + /// \exclude + template + constexpr detail::invoke_result_t and_then(F &&f) const && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : result(nullopt); + } + + /// \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. + /// \group map + /// \synopsis template auto map(F &&f); + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + detail::get_map_return map(F &&f) & + noexcept(noexcept(detail::invoke(std::forward(f), + std::declval()))) { + using result = detail::get_map_return; + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + /// \exclude + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + detail::get_map_return map(F &&f) & { + if (!has_value()) + return nullopt; + + detail::invoke(std::forward(f), **this); + return monostate{}; + } + + /// \exclude + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + detail::get_map_return map(F &&f) & { + using result = detail::get_map_return; + return (f.has_value() && has_value()) + ? detail::invoke(*std::forward(f), **this) + : result(nullopt); + } + + /// \exclude + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + detail::get_map_return map(F &&f) & { + if (!f.has_value() || !has_value()) + return nullopt; + + detail::invoke(*std::forward(f), **this); + return monostate{}; + } + + /// \exclude + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + detail::get_map_return map(F &&f) && { + using result = detail::get_map_return; + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : result(nullopt); + } + + /// \exclude + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + detail::get_map_return map(F &&f) && { + if (!has_value()) + return nullopt; + + detail::invoke(std::forward(f), std::move(**this)); + return monostate{}; + } + + /// \exclude + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + detail::get_map_return map(F &&f) && { + using result = detail::get_map_return; + return (f.has_value() && has_value()) + ? detail::invoke(*std::forward(f), std::move(**this)) + : result(nullopt); + } + + /// \exclude + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + detail::get_map_return map(F &&f) && { + if (!f.has_value() || !has_value()) + return nullopt; + + detail::invoke(*std::forward(f), std::move(**this)); + 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 & { + using result = detail::get_map_return; + return this->has_value() + ? result(detail::invoke(std::forward(f), **this)) + : result(nullopt); + } + + /// \exclude + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + detail::get_map_return map(F &&f) const & { + if (!has_value()) + return nullopt; + + detail::invoke(std::forward(f), **this); + return monostate{}; + } + + /// \exclude + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) const & { + using result = detail::get_map_return; + return (f.has_value() && has_value()) + ? detail::invoke(*std::forward(f), **this) + : result(nullopt); + } + + /// \exclude + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + detail::get_map_return map(F &&f) const & { + if (!f.has_value() || !has_value()) + return nullopt; + + detail::invoke(*std::forward(f), **this); + return monostate{}; + } + + /// \exclude + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) const && { + using result = detail::get_map_return; + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : result(nullopt); + } + + /// \exclude + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + detail::get_map_return map(F &&f) const && { + if (!has_value()) + return nullopt; + + detail::invoke(std::forward(f), std::move(**this)); + return monostate{}; + } + + /// \exclude + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) const && { + using result = detail::get_map_return; + return (f.has_value() && has_value()) + ? detail::invoke(*std::forward(f), std::move(**this)) + : result(nullopt); + } + + /// \exclude + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + detail::get_map_return map(F &&f) const && { + if (!f.has_value() || !has_value()) + return nullopt; + + detail::invoke(*std::forward(f), std::move(**this)); + return monostate{}; + } + + /// \brief Calls `f` if the optional is empty + /// \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)()`. + /// \group or_else + /// \synopsis template optional or_else (F &&f); + template * = nullptr> + optional TL_OPTIONAL_MSVC_2015_CONSTEXPR or_else(F &&f) & { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional TL_OPTIONAL_MSVC_2015_CONSTEXPR or_else(F &&f) & { + return has_value() ? *this : std::forward(f)(); + } + + /// \exclude + template * = nullptr> + optional or_else(F &&f) && { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional TL_OPTIONAL_MSVC_2015_CONSTEXPR or_else(F &&f) && { + return has_value() ? std::move(*this) : std::forward(f)(); + } + + /// \exclude + template * = nullptr> + optional or_else(F &&f) const & { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + /// \group or_else + /// \synopsis template optional or_else (F &&f) const; + template * = nullptr> + optional TL_OPTIONAL_MSVC_2015_CONSTEXPR or_else(F &&f) const & { + return has_value() ? *this : std::forward(f)(); + } + + /// \exclude + template * = nullptr> + optional or_else(F &&f) const && { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional or_else(F &&f) const && { + return has_value() ? std::move(*this) : std::forward(f)(); + } + + + /// \brief Maps the stored value with `f` if there is one, otherwise returns `u` + /// \details If there is a value stored, then `f` is called with `**this` and the value is returned. + /// Otherwise `u` is returned. + /// \group map_or + /// \synopsis template U map_or(F &&f, U &&u); + template U map_or(F &&f, U &&u) & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u); + } + + /// \exclude + template U map_or(F &&f, U &&u) && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u); + } + + /// \group map_or + /// \synopsis template U map_or(F &&f, U &&u) const; + template U map_or(F &&f, U &&u) const & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u); + } + + /// \exclude + template U map_or(F &&f, U &&u) const && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u); + } + + /// \brief Maps the stored value with `f` if there is one, otherwise calls `u` and returns the result. + /// \details If there is a value stored, then `f` is called with `**this` and the value is returned. + /// Otherwise `std::forward(u)()` is returned. + /// \synopsis template U map_or_else(F &&f, U &&u); + /// \group map_or_else + template U map_or_else(F &&f, U &&u) & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u)(); + } + + /// \exclude + template U map_or_else(F &&f, U &&u) && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u)(); + } + + /// \group map_or_else + /// \synopsis template U map_or_else(F &&f, U &&u) const; + template U map_or_else(F &&f, U &&u) const & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u)(); + } + + /// \exclude + template U map_or_else(F &&f, U &&u) const && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u)(); + } + + using value_type = T; + + /// Constructs an optional that does not contain a value. + /// \group ctor_empty + constexpr optional() noexcept = default; + + /// \group ctor_empty + 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; + new (std::addressof(this->m_value)) T(*rhs); + } + } + + // 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()) { + this->m_has_value = true; + new (std::addressof(this->m_value)) T(std::move(*rhs)); + } + } + + + /// Constructs the stored value in-place using the given arguments. + /// \group in_place + /// \synopsis template constexpr explicit optional(in_place_t, Args&&... args); + template + constexpr explicit optional( + detail::enable_if_t::value, in_place_t>, + Args &&... args) + : base(in_place, std::forward(args)...) {} + + /// \group in_place + /// \synopsis template \nconstexpr explicit optional(in_place_t, std::initializer_list&, Args&&... args); + template + TL_OPTIONAL_11_CONSTEXPR explicit optional( + detail::enable_if_t &, + Args &&...>::value, + in_place_t>, + std::initializer_list il, Args &&... args) { + this->m_has_value = true; + new (std::addressof(this->m_value)) T(il, std::forward(args)...); + } + + /// Constructs the stored value with `u`. + /// \synopsis template constexpr optional(U &&u); + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::enable_forward_value * = nullptr> + constexpr optional(U &&u) : base(in_place, std::forward(u)) {} + + /// \exclude + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::enable_forward_value * = nullptr> + constexpr explicit optional(U &&u) : base(in_place, std::forward(u)) {} + + /// Converting copy constructor. + /// \synopsis template optional(const optional &rhs); + template < + class U, detail::enable_from_other * = nullptr, + detail::enable_if_t::value> * = nullptr> + optional(const optional &rhs) { + this->m_has_value = true; + new (std::addressof(this->m_value)) T(*rhs); + } + + /// \exclude + template * = nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit optional(const optional &rhs) { + this->m_has_value = true; + new (std::addressof(this->m_value)) T(*rhs); + } + + /// Converting move constructor. + /// \synopsis template optional(optional &&rhs); + template < + class U, detail::enable_from_other * = nullptr, + detail::enable_if_t::value> * = nullptr> + optional(optional &&rhs) { + this->m_has_value = true; + new (std::addressof(this->m_value)) T(std::move(*rhs)); + } + + /// \exclude + template < + class U, detail::enable_from_other * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit optional(optional &&rhs) { + this->m_has_value = true; + new (std::addressof(this->m_value)) T(std::move(*rhs)); + } + + /// Destructor. + ~optional() = default; + + /// Assignment to empty. + /// + /// Destroys the current value if there is one. + optional &operator=(nullopt_t) noexcept { + if (has_value()) { + this->m_value.~T(); + this->m_has_value = false; + } + } + + // TODO conditionally delete, check exception guarantee + /// Copy assignment. + /// + /// Copies the value from `rhs` if there is one. Otherwise resets the stored value in `*this`. + optional &operator=(const optional &rhs) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = rhs.m_value; + } else { + this->m_value.~T(); + this->m_has_value = false; + } + } + + if (rhs.has_value()) { + new (std::addressof(this->m_value)) T(rhs.m_value); + this->m_has_value = true; + } + } + + // TODO conditionally delete, check exception guarantee + /// Move assignment. + /// + /// Moves the value from `rhs` if there is one. Otherwise resets the stored value in `*this`. + optional &operator=(optional &&rhs) noexcept( + std::is_nothrow_move_assignable::value + &&std::is_nothrow_move_constructible::value) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = std::move(rhs.m_value); + } else { + this->m_value.~T(); + this->m_has_value = false; + } + } + + if (rhs.has_value()) { + new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); + this->m_has_value = true; + } + } + + // TODO conditionally delete, check exception guarantee + /// Assigns the stored value from `u`, destroying the old value if there was one. + /// \synopsis optional &operator=(U &&u); + template * = nullptr> + optional &operator=(U &&u) { + if (has_value()) { + this->m_value = std::forward(u); + } else { + new (std::addressof(this->m_value)) T(std::forward(u)); + this->m_has_value = true; + } + } + + // TODO check exception guarantee + /// Converting copy assignment operator. + /// + /// Copies the value from `rhs` if there is one. Otherwise resets the stored value in `*this`. + /// \synopsis optional &operator=(const optional & rhs); + template * = nullptr> + optional &operator=(const optional &rhs) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = rhs.m_value; + } else { + this->m_value.~T(); + this->m_has_value = false; + } + } + + if (rhs.has_value()) { + new (std::addressof(this->m_value)) T(rhs.m_value); + this->m_has_value = true; + } + } + + // TODO check exception guarantee + /// Converting move assignment operator. + /// + /// Moves the value from `rhs` if there is one. Otherwise resets the stored value in `*this`. + /// \synopsis optional &operator=(optional && rhs); + template * = nullptr> + optional &operator=(optional &&rhs) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = std::move(rhs.m_value); + } else { + this->m_value.~T(); + this->m_has_value = false; + } + } + + if (rhs.has_value()) { + new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); + this->m_has_value = true; + } + } + + /// Constructs the value in-place, destroying the current one if there is one. + /// \group emplace + template T &emplace(Args &&... args) { + static_assert(std::is_constructible::value, + "T must be constructible with Args"); + + *this = nullopt; + new (std::addressof(this->m_value)) T(std::forward(args)...); + } + + /// \group emplace + /// \synopsis template \nT& emplace(std::initializer_list il, Args &&... args); + template + detail::enable_if_t< + std::is_constructible &, Args &&...>::value, + T &> + emplace(std::initializer_list il, Args &&... args) { + *this = nullopt; + new (std::addressof(this->m_value)) T(il, std::forward(args)...); + } + + /// Swaps this optional with the other. + /// + /// If neither optionals have a value, nothing happens. + /// If both have a value, the values are swapped. + /// If one has a value, it is moved to the other and the movee is left valueless. + void + swap(optional &rhs) noexcept(std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { + if (has_value()) { + if (rhs.has_value()) { + using std::swap; + swap(**this, *rhs); + } else { + new (&rhs.m_value) T(std::move(this->m_value)); + this->m_value.T::~T(); + } + } else if (rhs.has_value()) { + new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); + rhs.m_value.T::~T(); + } + } + + /// \returns a pointer to the stored value + /// \requires a value is stored + /// \group pointer + /// \synopsis constexpr const T *operator->() const; + constexpr const T *operator->() const { + return std::addressof(this->m_value); + } + + /// \group pointer + /// \synopsis constexpr T *operator->(); + TL_OPTIONAL_11_CONSTEXPR T *operator->() { + return std::addressof(this->m_value); + } + + /// \returns the stored value + /// \requires a value is stored + /// \group deref + /// \synopsis constexpr T &operator*(); + TL_OPTIONAL_11_CONSTEXPR T &operator*() & { return this->m_value; } + + /// \group deref + /// \synopsis constexpr const T &operator*() const; + constexpr const T &operator*() const & { return this->m_value; } + + /// \exclude + TL_OPTIONAL_11_CONSTEXPR T &&operator*() && { + return std::move(this->m_value); + } + + /// \exclude + constexpr const T &&operator*() const && { return std::move(this->m_value); } + + /// \returns whether or not the optional has a value + /// \group has_value + constexpr bool has_value() const noexcept { return this->m_has_value; } + + /// \group has_value + constexpr explicit operator bool() const noexcept { + return this->m_has_value; + } + + /// \returns the contained value if there is one, otherwise throws [bad_optional_access] + /// \group value + /// \synopsis constexpr T &value(); + TL_OPTIONAL_11_CONSTEXPR T &value() & { + if (has_value()) + return this->m_value; + throw bad_optional_access(); + } + /// \group value + /// \synopsis constexpr const T &value() const; + TL_OPTIONAL_11_CONSTEXPR const T &value() const & { + if (has_value()) + return this->m_value; + throw bad_optional_access(); + } + /// \exclude + TL_OPTIONAL_11_CONSTEXPR T &&value() && { + if (has_value()) + return std::move(this->m_value); + throw bad_optional_access(); + } + /// \exclude + constexpr const T &&value() const && { + if (has_value()) + return std::move(this->m_value); + throw bad_optional_access(); + } + + /// \returns the stored value if there is one, otherwise returns `u` + /// \group value_or + template constexpr T value_or(U &&u) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } + + /// \group value_or + template constexpr T value_or(U &&u) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } + + /// Destroys the stored value if one exists, making the optional empty + void reset() noexcept { + if (has_value()) { + this->m_value.~T(); + this->m_has_value = false; + } + } + + +}; + /// \group relop /// \brief Compares two optional objects /// \details If both optionals contain a value, they are compared with `T`s relational operators. @@ -499,6 +1259,8 @@ inline constexpr optional make_optional(std::initializer_list il, Args &&... args) { return optional(in_place, il, std::forward(args)...); } + +// template optional(T)->optional; } // namespace tl namespace std { @@ -512,679 +1274,3 @@ template struct hash> { } }; } // namespace std - -namespace tl { -/// \exclude -namespace detail { -template ::value> -struct optional_storage_base { - TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) {} - - template - TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base(in_place_t, U &&... u) noexcept - : m_value(std::forward(u)...), m_has_value(true) {} - - ~optional_storage_base() { - if (m_has_value) { - m_value.~T(); - m_has_value = false; - } - } - - struct dummy {}; - union { - dummy m_dummy; - T m_value; - }; - - bool m_has_value; -}; - -template struct optional_storage_base { - TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base() noexcept - : m_dummy(), m_has_value(false) {} - - template - TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base(in_place_t, - U &&... u) noexcept - : m_value(std::forward(u)...), m_has_value(true) {} - - ~optional_storage_base() = default; - - struct dummy {}; - union { - dummy m_dummy; - T m_value; - }; - - bool m_has_value = false; -}; -} // 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; - - /// 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; - new (std::addressof(this->m_value)) T(*rhs); - } - } - - // 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()) { - this->m_has_value = true; - new (std::addressof(this->m_value)) T(std::move(*rhs)); - } - } - template - constexpr explicit optional( - detail::enable_if_t::value, in_place_t>, - Args &&... args) - : base(in_place, std::forward(args)...) {} - template - TL_OPTIONAL_11_CONSTEXPR explicit optional( - detail::enable_if_t &, - Args &&...>::value, - in_place_t>, - std::initializer_list il, Args &&... args) { - this->m_has_value = true; - new (std::addressof(this->m_value)) T(il, std::forward(args)...); - } - - template < - class U = T, - detail::enable_if_t::value> * = nullptr, - detail::enable_forward_value * = nullptr> - constexpr optional(U &&u) : base(in_place, std::forward(u)) {} - - template < - class U = T, - detail::enable_if_t::value> * = nullptr, - detail::enable_forward_value * = nullptr> - constexpr explicit optional(U &&u) : base(in_place, std::forward(u)) {} - - template < - class U, detail::enable_from_other * = nullptr, - detail::enable_if_t::value> * = nullptr> - optional(const optional &rhs) { - this->m_has_value = true; - new (std::addressof(this->m_value)) T(*rhs); - } - - template * = nullptr, - detail::enable_if_t::value> * = - nullptr> - optional(const optional &rhs) { - this->m_has_value = true; - new (std::addressof(this->m_value)) T(*rhs); - } - - template < - class U, detail::enable_from_other * = nullptr, - detail::enable_if_t::value> * = nullptr> - optional(optional &&rhs) { - this->m_has_value = true; - new (std::addressof(this->m_value)) T(std::move(*rhs)); - } - - template < - class U, detail::enable_from_other * = nullptr, - detail::enable_if_t::value> * = nullptr> - explicit optional(optional &&rhs) { - this->m_has_value = true; - new (std::addressof(this->m_value)) T(std::move(*rhs)); - } - - ~optional() = default; - - optional &operator=(nullopt_t) noexcept { - if (has_value()) { - this->m_value.~T(); - this->m_has_value = false; - } - } - - // TODO conditionally delete, check exception guarantee - optional &operator=(const optional &rhs) { - if (has_value()) { - if (rhs.has_value()) { - this->m_value = rhs.m_value; - } else { - this->m_value.~T(); - this->m_has_value = false; - } - } - - if (rhs.has_value()) { - new (std::addressof(this->m_value)) T(rhs.m_value); - this->m_has_value = true; - } - } - - // TODO conditionally delete, check exception guarantee - optional &operator=(optional &&rhs) noexcept( - std::is_nothrow_move_assignable::value - &&std::is_nothrow_move_constructible::value) { - if (has_value()) { - if (rhs.has_value()) { - this->m_value = std::move(rhs.m_value); - } else { - this->m_value.~T(); - this->m_has_value = false; - } - } - - if (rhs.has_value()) { - new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); - this->m_has_value = true; - } - } - - // TODO conditionally delete, check exception guarantee - template * = nullptr> - optional &operator=(U &&u) { - if (has_value()) { - this->m_value = std::forward(u); - } else { - new (std::addressof(this->m_value)) T(std::forward(u)); - this->m_has_value = true; - } - } - - // TODO check exception guarantee - template * = nullptr> - optional &operator=(const optional &rhs) { - if (has_value()) { - if (rhs.has_value()) { - this->m_value = rhs.m_value; - } else { - this->m_value.~T(); - this->m_has_value = false; - } - } - - if (rhs.has_value()) { - new (std::addressof(this->m_value)) T(rhs.m_value); - this->m_has_value = true; - } - } - - // TODO check exception guarantee - template * = nullptr> - optional &operator=(optional &&rhs) { - if (has_value()) { - if (rhs.has_value()) { - this->m_value = std::move(rhs.m_value); - } else { - this->m_value.~T(); - this->m_has_value = false; - } - } - - if (rhs.has_value()) { - new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); - this->m_has_value = true; - } - } - - template T &emplace(Args &&... args) { - static_assert(std::is_constructible::value, - "T must be constructible with Args"); - - *this = nullopt; - new (std::addressof(this->m_value)) T(std::forward(args)...); - } - - template - detail::enable_if_t< - std::is_constructible &, Args &&...>::value, - T &> - emplace(std::initializer_list il, Args &&... args) { - *this = nullopt; - new (std::addressof(this->m_value)) T(il, std::forward(args)...); - } - - void - swap(optional &rhs) noexcept(std::is_nothrow_move_constructible::value - &&detail::is_nothrow_swappable::value) { - if (has_value()) { - if (rhs.has_value()) { - using std::swap; - swap(**this, *rhs); - } else { - new (&rhs.m_value) T(std::move(this->m_value)); - this->m_value.T::~T(); - } - } else if (rhs.has_value()) { - new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); - rhs.m_value.T::~T(); - } - } - - constexpr const T *operator->() const { - return std::addressof(this->m_value); - } - TL_OPTIONAL_11_CONSTEXPR T *operator->() { - return std::addressof(this->m_value); - } - constexpr const T &operator*() const & { return this->m_value; } - TL_OPTIONAL_11_CONSTEXPR T &operator*() & { return this->m_value; } - TL_OPTIONAL_11_CONSTEXPR T &&operator*() && { - return std::move(this->m_value); - } - constexpr const T &&operator*() const && { return std::move(this->m_value); } - constexpr explicit operator bool() const noexcept { - return this->m_has_value; - } - constexpr bool has_value() const noexcept { return this->m_has_value; } - TL_OPTIONAL_11_CONSTEXPR const T &value() const & { - if (has_value()) - return this->m_value; - throw bad_optional_access(); - } - TL_OPTIONAL_11_CONSTEXPR T &value() & { - if (has_value()) - return this->m_value; - throw bad_optional_access(); - } - TL_OPTIONAL_11_CONSTEXPR T &&value() && { - if (has_value()) - return std::move(this->m_value); - throw bad_optional_access(); - } - constexpr const T &&value() const && { - if (has_value()) - return std::move(this->m_value); - throw bad_optional_access(); - } - template constexpr T value_or(U &&u) const & { - static_assert(std::is_copy_constructible::value && - std::is_convertible::value, - "T must be copy constructible and convertible from U"); - return has_value() ? **this : static_cast(std::forward(u)); - } - template constexpr T value_or(U &&u) && { - static_assert(std::is_move_constructible::value && - std::is_convertible::value, - "T must be move constructible and convertible from U"); - return has_value() ? **this : static_cast(std::forward(u)); - } - - void reset() noexcept { - if (has_value()) { - this->m_value.~T(); - this->m_has_value = false; - } - } - - - /// \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; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : 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; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : result(nullopt); - } - - /// \group and_then - template - constexpr detail::invoke_result_t and_then(F &&f) const & { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(nullopt); - } - - /// \group and_then_rval - template - constexpr detail::invoke_result_t and_then(F &&f) const && { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : 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) & - noexcept(noexcept(detail::invoke(std::forward(f), - std::declval()))) { - using result = detail::get_map_return; - return has_value() ? detail::invoke(std::forward(f), **this) - : result(nullopt); - } - - template * = nullptr, - detail::enable_if_ret_void * = nullptr> - detail::get_map_return map(F &&f) & { - if (!has_value()) - return nullopt; - - detail::invoke(std::forward(f), **this); - return monostate{}; - } - - template * = nullptr, - detail::disable_if_ret_void * = nullptr> - detail::get_map_return map(F &&f) & { - using result = detail::get_map_return; - return (f.has_value() && has_value()) - ? detail::invoke(*std::forward(f), **this) - : result(nullopt); - } - - template * = nullptr, - detail::enable_if_ret_void * = nullptr> - detail::get_map_return map(F &&f) & { - if (!f.has_value() || !has_value()) - return nullopt; - - detail::invoke(*std::forward(f), **this); - 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) && { - using result = detail::get_map_return; - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : result(nullopt); - } - - template * = nullptr, - detail::enable_if_ret_void * = nullptr> - detail::get_map_return map(F &&f) && { - if (!has_value()) - return nullopt; - - detail::invoke(std::forward(f), std::move(**this)); - return monostate{}; - } - - template * = nullptr, - detail::disable_if_ret_void * = nullptr> - detail::get_map_return map(F &&f) && { - using result = detail::get_map_return; - return (f.has_value() && has_value()) - ? detail::invoke(*std::forward(f), std::move(**this)) - : result(nullopt); - } - - template * = nullptr, - detail::enable_if_ret_void * = nullptr> - detail::get_map_return map(F &&f) && { - if (!f.has_value() || !has_value()) - return nullopt; - - detail::invoke(*std::forward(f), std::move(**this)); - 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 & { - using result = detail::get_map_return; - return this->has_value() - ? result(detail::invoke(std::forward(f), **this)) - : result(nullopt); - } - - template * = nullptr, - detail::enable_if_ret_void * = nullptr> - detail::get_map_return map(F &&f) const & { - if (!has_value()) - return nullopt; - - detail::invoke(std::forward(f), **this); - return monostate{}; - } - - template * = nullptr, - detail::disable_if_ret_void * = nullptr> - constexpr detail::get_map_return map(F &&f) const & { - using result = detail::get_map_return; - return (f.has_value() && has_value()) - ? detail::invoke(*std::forward(f), **this) - : result(nullopt); - } - - template * = nullptr, - detail::enable_if_ret_void * = nullptr> - detail::get_map_return map(F &&f) const & { - if (!f.has_value() || !has_value()) - return nullopt; - - detail::invoke(*std::forward(f), **this); - 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 && { - using result = detail::get_map_return; - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : result(nullopt); - } - - template * = nullptr, - detail::enable_if_ret_void * = nullptr> - detail::get_map_return map(F &&f) const && { - if (!has_value()) - return nullopt; - - detail::invoke(std::forward(f), std::move(**this)); - return monostate{}; - } - - template * = nullptr, - detail::disable_if_ret_void * = nullptr> - constexpr detail::get_map_return map(F &&f) const && { - using result = detail::get_map_return; - return (f.has_value() && has_value()) - ? detail::invoke(*std::forward(f), std::move(**this)) - : result(nullopt); - } - - template * = nullptr, - detail::enable_if_ret_void * = nullptr> - detail::get_map_return map(F &&f) const && { - if (!f.has_value() || !has_value()) - return nullopt; - - detail::invoke(*std::forward(f), std::move(**this)); - 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()) - return *this; - - std::forward(f)(); - return nullopt; - } - - template * = nullptr> - optional TL_OPTIONAL_MSVC_2015_CONSTEXPR or_else(F &&f) & { - 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()) - return std::move(*this); - - std::forward(f)(); - return nullopt; - } - - template * = nullptr> - optional TL_OPTIONAL_MSVC_2015_CONSTEXPR or_else(F &&f) && { - 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()) - return *this; - - std::forward(f)(); - return nullopt; - } - - template * = nullptr> - optional TL_OPTIONAL_MSVC_2015_CONSTEXPR or_else(F &&f) const & { - 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()) - return std::move(*this); - - std::forward(f)(); - return nullopt; - } - - template * = nullptr> - optional or_else(F &&f) const && { - return has_value() ? std::move(*this) : std::forward(f)(); - } - - - /// \group map_or - /// \brief Maps the stored value with `f` if there is one, otherwise returns `u` - /// \details If there is a value stored, then `f` is called with `**this` and the value is returned. - /// Otherwise `u` is returned. - template U map_or(F &&f, U &&u) & { - return has_value() ? detail::invoke(std::forward(f), **this) - : std::forward(u); - } - - /// \group map_or_val - /// \brief Maps the stored value with `f` if there is one, otherwise returns `u` - /// \details If there is a value stored, then `f` is called with `std::move(**this)` and the value is returned. - /// Otherwise `u` is returned. - template U map_or(F &&f, U &&u) && { - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : std::forward(u); - } - - /// \group map_or - template U map_or(F &&f, U &&u) const & { - return has_value() ? detail::invoke(std::forward(f), **this) - : std::forward(u); - } - - /// \group map_or_val - template U map_or(F &&f, U &&u) const && { - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : std::forward(u); - } - - /// \group map_or_else - /// \brief Maps the stored value with `f` if there is one, otherwise calls `u` and returns the result. - /// \details If there is a value stored, then `f` is called with `**this` and the value is returned. - /// Otherwise `std::forward(u)()` is returned. - template U map_or_else(F &&f, U &&u) & { - return has_value() ? detail::invoke(std::forward(f), **this) - : std::forward(u)(); - } - - /// \group map_or_else_rval - /// \brief Maps the stored value with `f` if there is one, otherwise calls `u` and returns the result. - /// \details If there is a value stored, then `f` is called with `std::move(**this)` and the value is returned. - /// Otherwise `std::forward(u)()` is returned. - template U map_or_else(F &&f, U &&u) && { - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : std::forward(u)(); - } - - /// \group map_or_else - template U map_or_else(F &&f, U &&u) const & { - return has_value() ? detail::invoke(std::forward(f), **this) - : std::forward(u)(); - } - - /// \group map_or_else_rval - template U map_or_else(F &&f, U &&u) const && { - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : std::forward(u)(); - } -}; - -// template optional(T)->optional; -} // namespace tl