Files
pilotclient/src/blackmisc/optional.h
2017-01-11 01:28:37 +00:00

150 lines
4.4 KiB
C++

/* Copyright (C) 2013
* swift Project Community / Contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
* including this file, may be copied, modified, propagated, or distributed except according to the terms
* contained in the LICENSE file.
*/
//! \file
#ifndef BLACKMISC_OPTIONAL_H
#define BLACKMISC_OPTIONAL_H
#include "blackmisc/typetraits.h"
#include <QtGlobal>
#include <utility>
namespace BlackMisc
{
/*!
* Class which can directly contain zero or one object of type T, with pointer semantics.
*/
template <typename T>
class Optional
{
public:
//! Default constructor.
Optional() noexcept {}
//! Construct from a value.
Optional(T value) noexcept(std::is_nothrow_move_constructible<T>::value)
{
new (m_data.bytes) T(std::move(value));
m_isValid = true;
}
//! Construct from a nullptr, equivalent to default constructor.
Optional(std::nullptr_t) noexcept {}
//! Copy constructor.
Optional(const Optional &other) noexcept(std::is_nothrow_copy_constructible<T>::value)
{
if (other.m_isValid) { new (m_data.bytes) T(*other); }
m_isValid = other.m_isValid;
}
//! Move constructor.
Optional(Optional &&other) noexcept(std::is_nothrow_move_constructible<T>::value)
{
if (other.m_isValid) { new (m_data.bytes) T(std::move(*other)); }
m_isValid = other.m_isValid;
}
//! Assign a nullptr.
Optional &operator =(std::nullptr_t) noexcept
{
reset();
return *this;
}
//! Copy assignment.
Optional &operator =(const Optional &other) noexcept(std::is_nothrow_copy_constructible<T>::value)
{
reset();
if (other.m_isValid) { new (m_data.bytes) T(*other); }
m_isValid = other.m_isValid;
return *this;
}
//! Move assignment.
Optional &operator =(Optional &&other) noexcept(std::is_nothrow_move_constructible<T>::value)
{
reset();
if (other.m_isValid) { new (m_data.bytes) T(std::move(*other)); }
m_isValid = other.m_isValid;
return *this;
}
//! Destructor.
~Optional() { if (m_isValid) { (*this)->~T(); } }
//! Explicit cast to bool, true if this Optional contains a value.
explicit operator bool() const noexcept { return m_isValid; }
//! If object is valid, destroy to make it invalid.
void reset() noexcept
{
if (m_isValid) { (*this)->~T(); }
m_isValid = false;
}
//! Dereference operator, returns reference to contained value, undefined if there is no value contained.
T &operator *() { return dereference(); }
//! Dereference operator, returns reference to contained value, undefined if there is no value contained.
const T &operator *() const { return dereference(); }
//! Indirection operator, returns pointer to contained value, undefined if there is no value contained.
T *operator ->() { return &dereference(); }
//! Indirection operator, returns pointer to contained value, undefined if there is no value contained.
const T *operator ->() const { return &dereference(); }
private:
bool m_isValid = false;
T &dereference() { Q_ASSERT(m_isValid); return m_data.object; }
const T &dereference() const { Q_ASSERT(m_isValid); return m_data.object; }
union Data
{
Data() {}
~Data() {}
char bytes[sizeof(T)];
T object;
};
Data m_data;
};
/*!
* Efficient swap for two Optional objects.
*/
template <typename T>
void swap(Optional<T> &a, Optional<T> &b) noexcept(Private::is_nothrow_swappable<T, T>::value)
{
if (a)
{
if (b)
{
using std::swap;
swap(*a, *b);
}
else
{
b = std::move(a);
a = nullptr;
}
}
else if (b)
{
a = std::move(b);
b = nullptr;
}
}
} //namespace BlackMisc
#endif // guard