`std::optional` under C++14 v1DeepPtr: a deep-copying unique_ptr wrapper in C++Constrained number...

What makes Graph invariants so useful/important?

The magic money tree problem

Japan - Plan around max visa duration

Copenhagen passport control - US citizen

Why don't electron-positron collisions release infinite energy?

How can the DM most effectively choose 1 out of an odd number of players to be targeted by an attack or effect?

How to calculate implied correlation via observed market price (Margrabe option)

Why CLRS example on residual networks does not follows its formula?

Why are 150k or 200k jobs considered good when there are 300k+ births a month?

Schwarzchild Radius of the Universe

What do you call something that goes against the spirit of the law, but is legal when interpreting the law to the letter?

Is there a familial term for apples and pears?

N.B. ligature in Latex

Is it tax fraud for an individual to declare non-taxable revenue as taxable income? (US tax laws)

What typically incentivizes a professor to change jobs to a lower ranking university?

TGV timetables / schedules?

How does one intimidate enemies without having the capacity for violence?

Motorized valve interfering with button?

Why is this code 6.5x slower with optimizations enabled?

"which" command doesn't work / path of Safari?

Why can't I see bouncing of a switch on an oscilloscope?

Why has Russell's definition of numbers using equivalence classes been finally abandoned? ( If it has actually been abandoned).

Can an x86 CPU running in real mode be considered to be basically an 8086 CPU?

Can Medicine checks be used, with decent rolls, to completely mitigate the risk of death from ongoing damage?



`std::optional` under C++14 v1


DeepPtr: a deep-copying unique_ptr wrapper in C++Constrained number templateReinventing std::optionalNullable<T>; header-only value semantics for forward declared typesdeep_ptr<T>; a header-only, deep copying, value semantic smart pointer for optionally defined typesOptional<T> implementationLockedValue implementationSimplified std::thread implementation using pthreadsC++ std::array wrapperC++ template-based dimension-tagged type






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}







1












$begingroup$


This is a first version of an implementation of std::optional it is supposed to compile under C++14. The public interface of the class is complete, but there are still quite a few things missing. Only a few of the constructor availability traits are checked, none of the noexcept clauses are implemented, no non-member functions are implemented. Also I saw that most implementations that are out there split the storage and the public interface into two separate classes, or inherit from a base class. I wanted to get a baseline implementation working and tested and then move forward with maybe better abstractions internally.



What is there has been unit tested for most code-paths, or manually checked, some of the constraints are hard to verify. E.g. how to verify that a destructor was not called when the object is trivially destructable.



I am also still puzzling about a few of the signatures e.g.



    constexpr const T&& operator*() const&& noexcept { return std::move(*reinterpret_cast<const T*>(&storage_)); }


This seems to silently discard const by letting the user move the content out of the optional (if it is an rvalue).



The current code, including tests is available at (https://github.com/HarryDC/optional), I'm reinventing the wheel here for educational purposes, this touches a lot of areas that just don't come up in my normal use of C++. This was developed under Visual Studio, and spot checked on compiler explorer under different compilers.



#include <exception>
#include <initializer_list>
#include <utility>

namespace hs
{

// Missing from C++14
template< class From, class To >
constexpr bool is_convertible_v = std::is_convertible<From, To>::value;

template<class A, class B>
constexpr bool is_same_v = std::is_same<A, B>::value;

// Internals
namespace detail
{
template < typename T, typename std::enable_if_t<std::is_trivially_destructible<T>::value, int> = 0>
void destruct(T*) {}

template < typename T, typename std::enable_if_t < !std::is_trivially_destructible<T>::value, int > = 0 >
void destruct(T* t)
{
t->~T();
}


} // namespace detail

// Optional types
class bad_optional_access : public std::exception {};

struct nullopt_t
{
explicit nullopt_t() = default;
};
constexpr nullopt_t nullopt{};

struct in_place_t
{
explicit in_place_t() = default;
};
constexpr in_place_t in_place{};

// Public Class
template <class T>
class optional
{
public:
using value_type = T;

// Constructors

constexpr optional() noexcept = default;
constexpr optional(nullopt_t) noexcept {}

constexpr optional(const optional& other)
{
if (!other.has_value_) return;
new (&storage_) T(*other);
has_value_ = true;
}

constexpr optional(optional&& other)
{
if (!other.has_value_) return;
new (&storage_) T(std::move(*other));
has_value_ = true;
}

template < class U >
optional(const optional<U>& other)
{
if (!other.has_value()) return;
new (&storage_) T(*other);
has_value_ = true;
}

template < class U >
optional(optional<U>&& other)
{
if (!other.has_value()) return;
new (&storage_) T(std::move(*other));
has_value_ = true;
}

template< class... Args >
constexpr explicit optional(in_place_t, Args&& ... args)
{
new (&storage_) T(std::forward<Args>(args)...);
has_value_ = true;
}

template< class U, class... Args >
constexpr explicit optional(hs::in_place_t,
std::initializer_list<U> ilist,
Args&& ... args)
{
new (&storage_) T(std::forward<std::initializer_list<U>>(ilist), std::forward<Args>(args)...);
has_value_ = true;
}

template < class U = value_type,
typename std::enable_if_t < is_convertible_v<U, T>&&
!is_same_v<std::decay_t<U>, optional<T>>, int > = 0
>
constexpr optional(U && val)
{
new (&storage_) T(std::forward<U>(val));
has_value_ = true;
}

// Destructor
~optional()
{
if (has_value_) detail::destruct<T>(reinterpret_cast<T*>(&storage_));
}

// Operator =
optional& operator=(nullopt_t) noexcept
{
reset();
return *this;
}

// Don't know why the following two overloads (2/3) are separate from copy-op 5/6
constexpr optional& operator=(const optional& other)
{
if (other.has_value_)
{
if (has_value_)
{
**this = *other;
}
else
{
new (&storage_) T(*other);
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

constexpr optional& operator=(optional&& other) noexcept
{
if (other.has_value_)
{
if (has_value_)
{
**this = std::move(*other);
}
else
{
new (&storage_) T(std::move(*other));
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

template < class U = value_type,
typename std::enable_if_t < is_convertible_v<U, T>&&
!is_same_v<std::decay_t<U>, optional<T>>, int > = 0
>
optional & operator=(U && value)
{
if (has_value_)
{
**this = std::forward<U>(value);
}
else
{
new (&storage_) T(std::forward<U>(value));
has_value_ = true;
}
return *this;
}

template< class U >
optional& operator=(const optional<U>& other)
{
if (other.has_value())
{
if (has_value_)
{
**this = *other;
}
else
{
new (&storage_) T(*other);
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

template< class U >
optional& operator=(optional<U>&& other)
{
if (other.has_value())
{
if (has_value_)
{
**this = std::move(*other);
}
else
{
new (&storage_) T(std::move(*other));
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

// Operator ->, *
// TODO unit test ->

constexpr T* operator->() noexcept { return reinterpret_cast<T*>(&storage_); }
constexpr const T* operator->() const noexcept { return reinterpret_cast<const T*>(&storage_); }

constexpr T& operator*()& noexcept { return *reinterpret_cast<T*>(&storage_); }
constexpr const T& operator*()const& noexcept { return *reinterpret_cast<const T*>(&storage_); }

constexpr T&& operator*()&& noexcept { return std::move(*reinterpret_cast<T*>(&storage_)); }

// What does const in this context mean ??? How to test this
constexpr const T&& operator*() const&& noexcept { return std::move(*reinterpret_cast<const T*>(&storage_)); }

// operator bool, has_value()
constexpr operator bool() const noexcept { return has_value_; }
constexpr bool has_value() const noexcept { return has_value_; }

// value()

constexpr T& value()&
{
if (has_value_) return *reinterpret_cast<T*>(&storage_);
else throw bad_optional_access();
}

constexpr const T& value() const&
{
if (has_value_) return *reinterpret_cast<const T*>(&storage_);
else throw bad_optional_access();
}

// This is on an r-value Do we need to do anything different here ???
constexpr T&& value()&&
{
if (has_value_) return std::move(*reinterpret_cast<T*>(&storage_));
else throw bad_optional_access();
}

// This is on an r-value Do we need to do anything different here ???
// TODO unittest (HOW ???)
constexpr const T&& value() const&&
{
if (has_value_) return std::move(*reinterpret_cast<T*>(&storage_));
else throw bad_optional_access();
}

// value_or()
template <class U>
constexpr T value_or(U&& default_value) const&
{
return (has_value_) ? (**this) : static_cast<T>(std::forward<U>(default_value));
}

template <class U>
constexpr T value_or(U&& default_value)&&
{
return (has_value_) ? std::move(**this) : static_cast<T>(std::forward<U>(default_value));
}

// swap
void swap(optional& other)
{
if (has_value_ && other)
{
std::swap(**this, *other);
}
else if (has_value_)
{
other = std::move(*this);
reset();
}
else if (other)
{
*this = std::move(*other);
other.reset();
}
}


// reset
void reset() noexcept
{
if (has_value_) detail::destruct<T>(reinterpret_cast<T*>(&storage_));
has_value_ = false;
}

// emplace
template< class... Args >
T& emplace(Args&& ... args)
{
new (&storage_) T(std::forward<Args>(args)...);
has_value_ = true;
return **this;
}

template< class U, class... Args >
T& emplace(std::initializer_list<U> ilist, Args&& ... args)
{
new (&storage_) T(std::forward<std::initializer_list<U>>(ilist), std::forward<Args>(args)...);
has_value_ = true;
return **this;
}

private:
bool has_value_{ false };
typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
};
// TBD ...
// Non-member func
// comparators
// make_optional
// std::swap

// Helper Class
// std::hash
}
```









share|improve this question











$endgroup$












  • $begingroup$
    "This seems to silently discard const by letting the user move the content out of the optional (if it is an rvalue)." Can you explain your reasoning for why this silently discards const?
    $endgroup$
    – Justin
    Apr 2 at 2:51










  • $begingroup$
    I think i'm basically just confused about the overall signature and its semantics (i.e. learning experience), its a const function, called on an rvalue reference. It returns a forwarding reference (not an rvalue reference) to the value contained inside the (if I am getting the nomenclature right here). By returning the value of the optional using std::move it is marked as an rvalue. I think i'm missing the purpose of this signature. Maybe the comment thread here is not the right place to discuss this either ...
    $endgroup$
    – Harald Scheirich
    Apr 2 at 12:24




















1












$begingroup$


This is a first version of an implementation of std::optional it is supposed to compile under C++14. The public interface of the class is complete, but there are still quite a few things missing. Only a few of the constructor availability traits are checked, none of the noexcept clauses are implemented, no non-member functions are implemented. Also I saw that most implementations that are out there split the storage and the public interface into two separate classes, or inherit from a base class. I wanted to get a baseline implementation working and tested and then move forward with maybe better abstractions internally.



What is there has been unit tested for most code-paths, or manually checked, some of the constraints are hard to verify. E.g. how to verify that a destructor was not called when the object is trivially destructable.



I am also still puzzling about a few of the signatures e.g.



    constexpr const T&& operator*() const&& noexcept { return std::move(*reinterpret_cast<const T*>(&storage_)); }


This seems to silently discard const by letting the user move the content out of the optional (if it is an rvalue).



The current code, including tests is available at (https://github.com/HarryDC/optional), I'm reinventing the wheel here for educational purposes, this touches a lot of areas that just don't come up in my normal use of C++. This was developed under Visual Studio, and spot checked on compiler explorer under different compilers.



#include <exception>
#include <initializer_list>
#include <utility>

namespace hs
{

// Missing from C++14
template< class From, class To >
constexpr bool is_convertible_v = std::is_convertible<From, To>::value;

template<class A, class B>
constexpr bool is_same_v = std::is_same<A, B>::value;

// Internals
namespace detail
{
template < typename T, typename std::enable_if_t<std::is_trivially_destructible<T>::value, int> = 0>
void destruct(T*) {}

template < typename T, typename std::enable_if_t < !std::is_trivially_destructible<T>::value, int > = 0 >
void destruct(T* t)
{
t->~T();
}


} // namespace detail

// Optional types
class bad_optional_access : public std::exception {};

struct nullopt_t
{
explicit nullopt_t() = default;
};
constexpr nullopt_t nullopt{};

struct in_place_t
{
explicit in_place_t() = default;
};
constexpr in_place_t in_place{};

// Public Class
template <class T>
class optional
{
public:
using value_type = T;

// Constructors

constexpr optional() noexcept = default;
constexpr optional(nullopt_t) noexcept {}

constexpr optional(const optional& other)
{
if (!other.has_value_) return;
new (&storage_) T(*other);
has_value_ = true;
}

constexpr optional(optional&& other)
{
if (!other.has_value_) return;
new (&storage_) T(std::move(*other));
has_value_ = true;
}

template < class U >
optional(const optional<U>& other)
{
if (!other.has_value()) return;
new (&storage_) T(*other);
has_value_ = true;
}

template < class U >
optional(optional<U>&& other)
{
if (!other.has_value()) return;
new (&storage_) T(std::move(*other));
has_value_ = true;
}

template< class... Args >
constexpr explicit optional(in_place_t, Args&& ... args)
{
new (&storage_) T(std::forward<Args>(args)...);
has_value_ = true;
}

template< class U, class... Args >
constexpr explicit optional(hs::in_place_t,
std::initializer_list<U> ilist,
Args&& ... args)
{
new (&storage_) T(std::forward<std::initializer_list<U>>(ilist), std::forward<Args>(args)...);
has_value_ = true;
}

template < class U = value_type,
typename std::enable_if_t < is_convertible_v<U, T>&&
!is_same_v<std::decay_t<U>, optional<T>>, int > = 0
>
constexpr optional(U && val)
{
new (&storage_) T(std::forward<U>(val));
has_value_ = true;
}

// Destructor
~optional()
{
if (has_value_) detail::destruct<T>(reinterpret_cast<T*>(&storage_));
}

// Operator =
optional& operator=(nullopt_t) noexcept
{
reset();
return *this;
}

// Don't know why the following two overloads (2/3) are separate from copy-op 5/6
constexpr optional& operator=(const optional& other)
{
if (other.has_value_)
{
if (has_value_)
{
**this = *other;
}
else
{
new (&storage_) T(*other);
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

constexpr optional& operator=(optional&& other) noexcept
{
if (other.has_value_)
{
if (has_value_)
{
**this = std::move(*other);
}
else
{
new (&storage_) T(std::move(*other));
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

template < class U = value_type,
typename std::enable_if_t < is_convertible_v<U, T>&&
!is_same_v<std::decay_t<U>, optional<T>>, int > = 0
>
optional & operator=(U && value)
{
if (has_value_)
{
**this = std::forward<U>(value);
}
else
{
new (&storage_) T(std::forward<U>(value));
has_value_ = true;
}
return *this;
}

template< class U >
optional& operator=(const optional<U>& other)
{
if (other.has_value())
{
if (has_value_)
{
**this = *other;
}
else
{
new (&storage_) T(*other);
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

template< class U >
optional& operator=(optional<U>&& other)
{
if (other.has_value())
{
if (has_value_)
{
**this = std::move(*other);
}
else
{
new (&storage_) T(std::move(*other));
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

// Operator ->, *
// TODO unit test ->

constexpr T* operator->() noexcept { return reinterpret_cast<T*>(&storage_); }
constexpr const T* operator->() const noexcept { return reinterpret_cast<const T*>(&storage_); }

constexpr T& operator*()& noexcept { return *reinterpret_cast<T*>(&storage_); }
constexpr const T& operator*()const& noexcept { return *reinterpret_cast<const T*>(&storage_); }

constexpr T&& operator*()&& noexcept { return std::move(*reinterpret_cast<T*>(&storage_)); }

// What does const in this context mean ??? How to test this
constexpr const T&& operator*() const&& noexcept { return std::move(*reinterpret_cast<const T*>(&storage_)); }

// operator bool, has_value()
constexpr operator bool() const noexcept { return has_value_; }
constexpr bool has_value() const noexcept { return has_value_; }

// value()

constexpr T& value()&
{
if (has_value_) return *reinterpret_cast<T*>(&storage_);
else throw bad_optional_access();
}

constexpr const T& value() const&
{
if (has_value_) return *reinterpret_cast<const T*>(&storage_);
else throw bad_optional_access();
}

// This is on an r-value Do we need to do anything different here ???
constexpr T&& value()&&
{
if (has_value_) return std::move(*reinterpret_cast<T*>(&storage_));
else throw bad_optional_access();
}

// This is on an r-value Do we need to do anything different here ???
// TODO unittest (HOW ???)
constexpr const T&& value() const&&
{
if (has_value_) return std::move(*reinterpret_cast<T*>(&storage_));
else throw bad_optional_access();
}

// value_or()
template <class U>
constexpr T value_or(U&& default_value) const&
{
return (has_value_) ? (**this) : static_cast<T>(std::forward<U>(default_value));
}

template <class U>
constexpr T value_or(U&& default_value)&&
{
return (has_value_) ? std::move(**this) : static_cast<T>(std::forward<U>(default_value));
}

// swap
void swap(optional& other)
{
if (has_value_ && other)
{
std::swap(**this, *other);
}
else if (has_value_)
{
other = std::move(*this);
reset();
}
else if (other)
{
*this = std::move(*other);
other.reset();
}
}


// reset
void reset() noexcept
{
if (has_value_) detail::destruct<T>(reinterpret_cast<T*>(&storage_));
has_value_ = false;
}

// emplace
template< class... Args >
T& emplace(Args&& ... args)
{
new (&storage_) T(std::forward<Args>(args)...);
has_value_ = true;
return **this;
}

template< class U, class... Args >
T& emplace(std::initializer_list<U> ilist, Args&& ... args)
{
new (&storage_) T(std::forward<std::initializer_list<U>>(ilist), std::forward<Args>(args)...);
has_value_ = true;
return **this;
}

private:
bool has_value_{ false };
typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
};
// TBD ...
// Non-member func
// comparators
// make_optional
// std::swap

// Helper Class
// std::hash
}
```









share|improve this question











$endgroup$












  • $begingroup$
    "This seems to silently discard const by letting the user move the content out of the optional (if it is an rvalue)." Can you explain your reasoning for why this silently discards const?
    $endgroup$
    – Justin
    Apr 2 at 2:51










  • $begingroup$
    I think i'm basically just confused about the overall signature and its semantics (i.e. learning experience), its a const function, called on an rvalue reference. It returns a forwarding reference (not an rvalue reference) to the value contained inside the (if I am getting the nomenclature right here). By returning the value of the optional using std::move it is marked as an rvalue. I think i'm missing the purpose of this signature. Maybe the comment thread here is not the right place to discuss this either ...
    $endgroup$
    – Harald Scheirich
    Apr 2 at 12:24
















1












1








1





$begingroup$


This is a first version of an implementation of std::optional it is supposed to compile under C++14. The public interface of the class is complete, but there are still quite a few things missing. Only a few of the constructor availability traits are checked, none of the noexcept clauses are implemented, no non-member functions are implemented. Also I saw that most implementations that are out there split the storage and the public interface into two separate classes, or inherit from a base class. I wanted to get a baseline implementation working and tested and then move forward with maybe better abstractions internally.



What is there has been unit tested for most code-paths, or manually checked, some of the constraints are hard to verify. E.g. how to verify that a destructor was not called when the object is trivially destructable.



I am also still puzzling about a few of the signatures e.g.



    constexpr const T&& operator*() const&& noexcept { return std::move(*reinterpret_cast<const T*>(&storage_)); }


This seems to silently discard const by letting the user move the content out of the optional (if it is an rvalue).



The current code, including tests is available at (https://github.com/HarryDC/optional), I'm reinventing the wheel here for educational purposes, this touches a lot of areas that just don't come up in my normal use of C++. This was developed under Visual Studio, and spot checked on compiler explorer under different compilers.



#include <exception>
#include <initializer_list>
#include <utility>

namespace hs
{

// Missing from C++14
template< class From, class To >
constexpr bool is_convertible_v = std::is_convertible<From, To>::value;

template<class A, class B>
constexpr bool is_same_v = std::is_same<A, B>::value;

// Internals
namespace detail
{
template < typename T, typename std::enable_if_t<std::is_trivially_destructible<T>::value, int> = 0>
void destruct(T*) {}

template < typename T, typename std::enable_if_t < !std::is_trivially_destructible<T>::value, int > = 0 >
void destruct(T* t)
{
t->~T();
}


} // namespace detail

// Optional types
class bad_optional_access : public std::exception {};

struct nullopt_t
{
explicit nullopt_t() = default;
};
constexpr nullopt_t nullopt{};

struct in_place_t
{
explicit in_place_t() = default;
};
constexpr in_place_t in_place{};

// Public Class
template <class T>
class optional
{
public:
using value_type = T;

// Constructors

constexpr optional() noexcept = default;
constexpr optional(nullopt_t) noexcept {}

constexpr optional(const optional& other)
{
if (!other.has_value_) return;
new (&storage_) T(*other);
has_value_ = true;
}

constexpr optional(optional&& other)
{
if (!other.has_value_) return;
new (&storage_) T(std::move(*other));
has_value_ = true;
}

template < class U >
optional(const optional<U>& other)
{
if (!other.has_value()) return;
new (&storage_) T(*other);
has_value_ = true;
}

template < class U >
optional(optional<U>&& other)
{
if (!other.has_value()) return;
new (&storage_) T(std::move(*other));
has_value_ = true;
}

template< class... Args >
constexpr explicit optional(in_place_t, Args&& ... args)
{
new (&storage_) T(std::forward<Args>(args)...);
has_value_ = true;
}

template< class U, class... Args >
constexpr explicit optional(hs::in_place_t,
std::initializer_list<U> ilist,
Args&& ... args)
{
new (&storage_) T(std::forward<std::initializer_list<U>>(ilist), std::forward<Args>(args)...);
has_value_ = true;
}

template < class U = value_type,
typename std::enable_if_t < is_convertible_v<U, T>&&
!is_same_v<std::decay_t<U>, optional<T>>, int > = 0
>
constexpr optional(U && val)
{
new (&storage_) T(std::forward<U>(val));
has_value_ = true;
}

// Destructor
~optional()
{
if (has_value_) detail::destruct<T>(reinterpret_cast<T*>(&storage_));
}

// Operator =
optional& operator=(nullopt_t) noexcept
{
reset();
return *this;
}

// Don't know why the following two overloads (2/3) are separate from copy-op 5/6
constexpr optional& operator=(const optional& other)
{
if (other.has_value_)
{
if (has_value_)
{
**this = *other;
}
else
{
new (&storage_) T(*other);
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

constexpr optional& operator=(optional&& other) noexcept
{
if (other.has_value_)
{
if (has_value_)
{
**this = std::move(*other);
}
else
{
new (&storage_) T(std::move(*other));
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

template < class U = value_type,
typename std::enable_if_t < is_convertible_v<U, T>&&
!is_same_v<std::decay_t<U>, optional<T>>, int > = 0
>
optional & operator=(U && value)
{
if (has_value_)
{
**this = std::forward<U>(value);
}
else
{
new (&storage_) T(std::forward<U>(value));
has_value_ = true;
}
return *this;
}

template< class U >
optional& operator=(const optional<U>& other)
{
if (other.has_value())
{
if (has_value_)
{
**this = *other;
}
else
{
new (&storage_) T(*other);
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

template< class U >
optional& operator=(optional<U>&& other)
{
if (other.has_value())
{
if (has_value_)
{
**this = std::move(*other);
}
else
{
new (&storage_) T(std::move(*other));
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

// Operator ->, *
// TODO unit test ->

constexpr T* operator->() noexcept { return reinterpret_cast<T*>(&storage_); }
constexpr const T* operator->() const noexcept { return reinterpret_cast<const T*>(&storage_); }

constexpr T& operator*()& noexcept { return *reinterpret_cast<T*>(&storage_); }
constexpr const T& operator*()const& noexcept { return *reinterpret_cast<const T*>(&storage_); }

constexpr T&& operator*()&& noexcept { return std::move(*reinterpret_cast<T*>(&storage_)); }

// What does const in this context mean ??? How to test this
constexpr const T&& operator*() const&& noexcept { return std::move(*reinterpret_cast<const T*>(&storage_)); }

// operator bool, has_value()
constexpr operator bool() const noexcept { return has_value_; }
constexpr bool has_value() const noexcept { return has_value_; }

// value()

constexpr T& value()&
{
if (has_value_) return *reinterpret_cast<T*>(&storage_);
else throw bad_optional_access();
}

constexpr const T& value() const&
{
if (has_value_) return *reinterpret_cast<const T*>(&storage_);
else throw bad_optional_access();
}

// This is on an r-value Do we need to do anything different here ???
constexpr T&& value()&&
{
if (has_value_) return std::move(*reinterpret_cast<T*>(&storage_));
else throw bad_optional_access();
}

// This is on an r-value Do we need to do anything different here ???
// TODO unittest (HOW ???)
constexpr const T&& value() const&&
{
if (has_value_) return std::move(*reinterpret_cast<T*>(&storage_));
else throw bad_optional_access();
}

// value_or()
template <class U>
constexpr T value_or(U&& default_value) const&
{
return (has_value_) ? (**this) : static_cast<T>(std::forward<U>(default_value));
}

template <class U>
constexpr T value_or(U&& default_value)&&
{
return (has_value_) ? std::move(**this) : static_cast<T>(std::forward<U>(default_value));
}

// swap
void swap(optional& other)
{
if (has_value_ && other)
{
std::swap(**this, *other);
}
else if (has_value_)
{
other = std::move(*this);
reset();
}
else if (other)
{
*this = std::move(*other);
other.reset();
}
}


// reset
void reset() noexcept
{
if (has_value_) detail::destruct<T>(reinterpret_cast<T*>(&storage_));
has_value_ = false;
}

// emplace
template< class... Args >
T& emplace(Args&& ... args)
{
new (&storage_) T(std::forward<Args>(args)...);
has_value_ = true;
return **this;
}

template< class U, class... Args >
T& emplace(std::initializer_list<U> ilist, Args&& ... args)
{
new (&storage_) T(std::forward<std::initializer_list<U>>(ilist), std::forward<Args>(args)...);
has_value_ = true;
return **this;
}

private:
bool has_value_{ false };
typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
};
// TBD ...
// Non-member func
// comparators
// make_optional
// std::swap

// Helper Class
// std::hash
}
```









share|improve this question











$endgroup$




This is a first version of an implementation of std::optional it is supposed to compile under C++14. The public interface of the class is complete, but there are still quite a few things missing. Only a few of the constructor availability traits are checked, none of the noexcept clauses are implemented, no non-member functions are implemented. Also I saw that most implementations that are out there split the storage and the public interface into two separate classes, or inherit from a base class. I wanted to get a baseline implementation working and tested and then move forward with maybe better abstractions internally.



What is there has been unit tested for most code-paths, or manually checked, some of the constraints are hard to verify. E.g. how to verify that a destructor was not called when the object is trivially destructable.



I am also still puzzling about a few of the signatures e.g.



    constexpr const T&& operator*() const&& noexcept { return std::move(*reinterpret_cast<const T*>(&storage_)); }


This seems to silently discard const by letting the user move the content out of the optional (if it is an rvalue).



The current code, including tests is available at (https://github.com/HarryDC/optional), I'm reinventing the wheel here for educational purposes, this touches a lot of areas that just don't come up in my normal use of C++. This was developed under Visual Studio, and spot checked on compiler explorer under different compilers.



#include <exception>
#include <initializer_list>
#include <utility>

namespace hs
{

// Missing from C++14
template< class From, class To >
constexpr bool is_convertible_v = std::is_convertible<From, To>::value;

template<class A, class B>
constexpr bool is_same_v = std::is_same<A, B>::value;

// Internals
namespace detail
{
template < typename T, typename std::enable_if_t<std::is_trivially_destructible<T>::value, int> = 0>
void destruct(T*) {}

template < typename T, typename std::enable_if_t < !std::is_trivially_destructible<T>::value, int > = 0 >
void destruct(T* t)
{
t->~T();
}


} // namespace detail

// Optional types
class bad_optional_access : public std::exception {};

struct nullopt_t
{
explicit nullopt_t() = default;
};
constexpr nullopt_t nullopt{};

struct in_place_t
{
explicit in_place_t() = default;
};
constexpr in_place_t in_place{};

// Public Class
template <class T>
class optional
{
public:
using value_type = T;

// Constructors

constexpr optional() noexcept = default;
constexpr optional(nullopt_t) noexcept {}

constexpr optional(const optional& other)
{
if (!other.has_value_) return;
new (&storage_) T(*other);
has_value_ = true;
}

constexpr optional(optional&& other)
{
if (!other.has_value_) return;
new (&storage_) T(std::move(*other));
has_value_ = true;
}

template < class U >
optional(const optional<U>& other)
{
if (!other.has_value()) return;
new (&storage_) T(*other);
has_value_ = true;
}

template < class U >
optional(optional<U>&& other)
{
if (!other.has_value()) return;
new (&storage_) T(std::move(*other));
has_value_ = true;
}

template< class... Args >
constexpr explicit optional(in_place_t, Args&& ... args)
{
new (&storage_) T(std::forward<Args>(args)...);
has_value_ = true;
}

template< class U, class... Args >
constexpr explicit optional(hs::in_place_t,
std::initializer_list<U> ilist,
Args&& ... args)
{
new (&storage_) T(std::forward<std::initializer_list<U>>(ilist), std::forward<Args>(args)...);
has_value_ = true;
}

template < class U = value_type,
typename std::enable_if_t < is_convertible_v<U, T>&&
!is_same_v<std::decay_t<U>, optional<T>>, int > = 0
>
constexpr optional(U && val)
{
new (&storage_) T(std::forward<U>(val));
has_value_ = true;
}

// Destructor
~optional()
{
if (has_value_) detail::destruct<T>(reinterpret_cast<T*>(&storage_));
}

// Operator =
optional& operator=(nullopt_t) noexcept
{
reset();
return *this;
}

// Don't know why the following two overloads (2/3) are separate from copy-op 5/6
constexpr optional& operator=(const optional& other)
{
if (other.has_value_)
{
if (has_value_)
{
**this = *other;
}
else
{
new (&storage_) T(*other);
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

constexpr optional& operator=(optional&& other) noexcept
{
if (other.has_value_)
{
if (has_value_)
{
**this = std::move(*other);
}
else
{
new (&storage_) T(std::move(*other));
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

template < class U = value_type,
typename std::enable_if_t < is_convertible_v<U, T>&&
!is_same_v<std::decay_t<U>, optional<T>>, int > = 0
>
optional & operator=(U && value)
{
if (has_value_)
{
**this = std::forward<U>(value);
}
else
{
new (&storage_) T(std::forward<U>(value));
has_value_ = true;
}
return *this;
}

template< class U >
optional& operator=(const optional<U>& other)
{
if (other.has_value())
{
if (has_value_)
{
**this = *other;
}
else
{
new (&storage_) T(*other);
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

template< class U >
optional& operator=(optional<U>&& other)
{
if (other.has_value())
{
if (has_value_)
{
**this = std::move(*other);
}
else
{
new (&storage_) T(std::move(*other));
has_value_ = true;
}
}
else
{
reset();
}
return *this;
}

// Operator ->, *
// TODO unit test ->

constexpr T* operator->() noexcept { return reinterpret_cast<T*>(&storage_); }
constexpr const T* operator->() const noexcept { return reinterpret_cast<const T*>(&storage_); }

constexpr T& operator*()& noexcept { return *reinterpret_cast<T*>(&storage_); }
constexpr const T& operator*()const& noexcept { return *reinterpret_cast<const T*>(&storage_); }

constexpr T&& operator*()&& noexcept { return std::move(*reinterpret_cast<T*>(&storage_)); }

// What does const in this context mean ??? How to test this
constexpr const T&& operator*() const&& noexcept { return std::move(*reinterpret_cast<const T*>(&storage_)); }

// operator bool, has_value()
constexpr operator bool() const noexcept { return has_value_; }
constexpr bool has_value() const noexcept { return has_value_; }

// value()

constexpr T& value()&
{
if (has_value_) return *reinterpret_cast<T*>(&storage_);
else throw bad_optional_access();
}

constexpr const T& value() const&
{
if (has_value_) return *reinterpret_cast<const T*>(&storage_);
else throw bad_optional_access();
}

// This is on an r-value Do we need to do anything different here ???
constexpr T&& value()&&
{
if (has_value_) return std::move(*reinterpret_cast<T*>(&storage_));
else throw bad_optional_access();
}

// This is on an r-value Do we need to do anything different here ???
// TODO unittest (HOW ???)
constexpr const T&& value() const&&
{
if (has_value_) return std::move(*reinterpret_cast<T*>(&storage_));
else throw bad_optional_access();
}

// value_or()
template <class U>
constexpr T value_or(U&& default_value) const&
{
return (has_value_) ? (**this) : static_cast<T>(std::forward<U>(default_value));
}

template <class U>
constexpr T value_or(U&& default_value)&&
{
return (has_value_) ? std::move(**this) : static_cast<T>(std::forward<U>(default_value));
}

// swap
void swap(optional& other)
{
if (has_value_ && other)
{
std::swap(**this, *other);
}
else if (has_value_)
{
other = std::move(*this);
reset();
}
else if (other)
{
*this = std::move(*other);
other.reset();
}
}


// reset
void reset() noexcept
{
if (has_value_) detail::destruct<T>(reinterpret_cast<T*>(&storage_));
has_value_ = false;
}

// emplace
template< class... Args >
T& emplace(Args&& ... args)
{
new (&storage_) T(std::forward<Args>(args)...);
has_value_ = true;
return **this;
}

template< class U, class... Args >
T& emplace(std::initializer_list<U> ilist, Args&& ... args)
{
new (&storage_) T(std::forward<std::initializer_list<U>>(ilist), std::forward<Args>(args)...);
has_value_ = true;
return **this;
}

private:
bool has_value_{ false };
typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
};
// TBD ...
// Non-member func
// comparators
// make_optional
// std::swap

// Helper Class
// std::hash
}
```






c++ reinventing-the-wheel c++14 optional






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Apr 1 at 18:31









200_success

131k17157422




131k17157422










asked Apr 1 at 17:37









Harald ScheirichHarald Scheirich

1,399518




1,399518












  • $begingroup$
    "This seems to silently discard const by letting the user move the content out of the optional (if it is an rvalue)." Can you explain your reasoning for why this silently discards const?
    $endgroup$
    – Justin
    Apr 2 at 2:51










  • $begingroup$
    I think i'm basically just confused about the overall signature and its semantics (i.e. learning experience), its a const function, called on an rvalue reference. It returns a forwarding reference (not an rvalue reference) to the value contained inside the (if I am getting the nomenclature right here). By returning the value of the optional using std::move it is marked as an rvalue. I think i'm missing the purpose of this signature. Maybe the comment thread here is not the right place to discuss this either ...
    $endgroup$
    – Harald Scheirich
    Apr 2 at 12:24




















  • $begingroup$
    "This seems to silently discard const by letting the user move the content out of the optional (if it is an rvalue)." Can you explain your reasoning for why this silently discards const?
    $endgroup$
    – Justin
    Apr 2 at 2:51










  • $begingroup$
    I think i'm basically just confused about the overall signature and its semantics (i.e. learning experience), its a const function, called on an rvalue reference. It returns a forwarding reference (not an rvalue reference) to the value contained inside the (if I am getting the nomenclature right here). By returning the value of the optional using std::move it is marked as an rvalue. I think i'm missing the purpose of this signature. Maybe the comment thread here is not the right place to discuss this either ...
    $endgroup$
    – Harald Scheirich
    Apr 2 at 12:24


















$begingroup$
"This seems to silently discard const by letting the user move the content out of the optional (if it is an rvalue)." Can you explain your reasoning for why this silently discards const?
$endgroup$
– Justin
Apr 2 at 2:51




$begingroup$
"This seems to silently discard const by letting the user move the content out of the optional (if it is an rvalue)." Can you explain your reasoning for why this silently discards const?
$endgroup$
– Justin
Apr 2 at 2:51












$begingroup$
I think i'm basically just confused about the overall signature and its semantics (i.e. learning experience), its a const function, called on an rvalue reference. It returns a forwarding reference (not an rvalue reference) to the value contained inside the (if I am getting the nomenclature right here). By returning the value of the optional using std::move it is marked as an rvalue. I think i'm missing the purpose of this signature. Maybe the comment thread here is not the right place to discuss this either ...
$endgroup$
– Harald Scheirich
Apr 2 at 12:24






$begingroup$
I think i'm basically just confused about the overall signature and its semantics (i.e. learning experience), its a const function, called on an rvalue reference. It returns a forwarding reference (not an rvalue reference) to the value contained inside the (if I am getting the nomenclature right here). By returning the value of the optional using std::move it is marked as an rvalue. I think i'm missing the purpose of this signature. Maybe the comment thread here is not the right place to discuss this either ...
$endgroup$
– Harald Scheirich
Apr 2 at 12:24












0






active

oldest

votes












Your Answer





StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");

StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216669%2fstdoptional-under-c14-v1%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























0






active

oldest

votes








0






active

oldest

votes









active

oldest

votes






active

oldest

votes
















draft saved

draft discarded




















































Thanks for contributing an answer to Code Review Stack Exchange!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


Use MathJax to format equations. MathJax reference.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216669%2fstdoptional-under-c14-v1%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

is 'sed' thread safeWhat should someone know about using Python scripts in the shell?Nexenta bash script uses...

How do i solve the “ No module named 'mlxtend' ” issue on Jupyter?

Pilgersdorf Inhaltsverzeichnis Geografie | Geschichte | Bevölkerungsentwicklung | Politik | Kultur...