Created
September 16, 2023 18:31
-
-
Save jeremy-rifkin/a0460ed16effab44241dedcd8b6f3987 to your computer and use it in GitHub Desktop.
C++11 optional implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef OPTIONAL_HPP | |
#define OPTIONAL_HPP | |
#include <memory> | |
#include <new> | |
#include <stdexcept> | |
#include <type_traits> | |
#include <utility> | |
struct nullopt_t {}; | |
static constexpr nullopt_t nullopt; | |
template<typename T, typename std::enable_if<!std::is_same<typename std::decay<T>::type, void>::value, int>::type = 0> | |
class optional { | |
bool holds_value = false; | |
union { | |
T uvalue; | |
}; | |
public: | |
// clang-tidy false positive | |
// NOLINTNEXTLINE(modernize-use-equals-default) | |
optional() noexcept {} | |
optional(nullopt_t) noexcept {} | |
~optional() { | |
reset(); | |
} | |
optional(const optional& other) : holds_value(other.holds_value) { | |
if(holds_value) { | |
new (static_cast<void*>(std::addressof(uvalue))) T(other.uvalue); | |
} | |
} | |
optional(optional&& other) noexcept(std::is_nothrow_move_constructible<T>::value) : holds_value(other.holds_value) { | |
if(holds_value) { | |
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue)); | |
} | |
} | |
optional& operator=(const optional& other) { | |
optional copy(other); | |
swap(*this, copy); | |
return *this; | |
} | |
optional& operator=(optional&& other) noexcept( | |
std::is_nothrow_move_assignable<T>::value && std::is_nothrow_move_constructible<T>::value | |
) { | |
reset(); | |
if(other.holds_value) { | |
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue)); | |
holds_value = true; | |
} | |
return *this; | |
} | |
template< | |
typename U = T, | |
typename std::enable_if<!std::is_same<typename std::decay<U>::type, optional<T>>::value, int>::type = 0 | |
> | |
// clang-tidy false positive | |
// NOLINTNEXTLINE(bugprone-forwarding-reference-overload) | |
optional(U&& value) : holds_value(true) { | |
new (static_cast<void*>(std::addressof(uvalue))) T(std::forward<U>(value)); | |
} | |
template< | |
typename U = T, | |
typename std::enable_if<!std::is_same<typename std::decay<U>::type, optional<T>>::value, int>::type = 0 | |
> | |
optional& operator=(U&& value) { | |
if(holds_value) { | |
uvalue = std::forward<U>(value); | |
} else { | |
new (static_cast<void*>(std::addressof(uvalue))) T(std::forward<U>(value)); | |
holds_value = true; | |
} | |
return *this; | |
} | |
optional& operator=(nullopt_t) noexcept { | |
reset(); | |
return *this; | |
} | |
void swap(optional& other) { | |
if(holds_value && other.holds_value) { | |
std::swap(uvalue, other.uvalue); | |
} else if(holds_value && !other.holds_value) { | |
new (&other.uvalue) T(std::move(uvalue)); | |
uvalue.~T(); | |
} else if(!holds_value && other.holds_value) { | |
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue)); | |
other.uvalue.~T(); | |
} | |
std::swap(holds_value, other.holds_value); | |
} | |
bool has_value() const { | |
return holds_value; | |
} | |
operator bool() const { | |
return holds_value; | |
} | |
void reset() { | |
if(holds_value) { | |
uvalue.~T(); | |
} | |
holds_value = false; | |
} | |
T& unwrap() & { | |
if(!holds_value) { | |
throw std::runtime_error{"Optional does not contain a value"}; | |
} | |
return uvalue; | |
} | |
const T& unwrap() const & { | |
if(!holds_value) { | |
throw std::runtime_error{"Optional does not contain a value"}; | |
} | |
return uvalue; | |
} | |
T&& unwrap() && { | |
if(!holds_value) { | |
throw std::runtime_error{"Optional does not contain a value"}; | |
} | |
return std::move(uvalue); | |
} | |
const T&& unwrap() const && { | |
if(!holds_value) { | |
throw std::runtime_error{"Optional does not contain a value"}; | |
} | |
return std::move(uvalue); | |
} | |
template<typename U> | |
T value_or(U&& default_value) const & { | |
return holds_value ? uvalue : static_cast<T>(std::forward<U>(default_value)); | |
} | |
template<typename U> | |
T value_or(U&& default_value) && { | |
return holds_value ? std::move(uvalue) : static_cast<T>(std::forward<U>(default_value)); | |
} | |
}; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment