Skip to content

Instantly share code, notes, and snippets.

@minya
Created October 15, 2019 15:20
Show Gist options
  • Save minya/7f146ddb1a6f61e87e32df1bc097c336 to your computer and use it in GitHub Desktop.
Save minya/7f146ddb1a6f61e87e32df1bc097c336 to your computer and use it in GitHub Desktop.
#include <memory>
// Исключение этого типа должно генерироваться при обращении к "пустому" Optional в функции Value
struct BadOptionalAccess {
};
template <typename T>
class Optional {
private:
// alignas нужен для правильного выравнивания блока памяти
alignas(T) unsigned char data[sizeof(T)];
bool defined = false;
public:
Optional() = default;
Optional(const T& elem) {
new (data) T(elem);
defined = true;
}
Optional(T&& elem):
defined(true) {
new (data) T(std::move(elem));
}
Optional(const Optional& other) {
if (other.HasValue()) {
new (data) T(other.Value());
defined = true;
}
}
Optional(Optional&& other) : defined(other.defined) {
if (other.defined) {
new (data) T(std::move(other.Value()));
}
}
Optional& operator=(const T& elem) {
if (!defined) {
new (data) T(elem);
defined = true;
} else {
*reinterpret_cast<T*>(data) = elem;
}
return *this;
}
Optional& operator=(T&& elem) {
if (HasValue()) {
Reset();
}
*reinterpret_cast<T*>(data) = std::move(elem);
defined = true;
return *this;
}
Optional& operator=(const Optional& other) {
if (!defined) {
if (other.defined) {
new (data) T(other.Value());
defined = true;
}
} else {
if (!other.defined) {
Reset();
} else {
*reinterpret_cast<T*>(data) = other.Value();
}
}
return *this;
}
Optional& operator=(Optional&& other) {
if (defined) {
Reset();
}
if (other.defined) {
*reinterpret_cast<T*>(data) = std::move(*other);
defined = true;
other.defined = false;
}
return *this;
}
bool HasValue() const {
return defined;
}
// Эти операторы не должны делать никаких проверок на пустоту.
// Проверки остаются на совести программиста.
T& operator*() {
return *reinterpret_cast<T*>(data);
}
const T& operator*() const {
return *reinterpret_cast<const T*>(data);
}
T* operator->() {
return reinterpret_cast<T*>(data);
}
const T* operator->() const {
return reinterpret_cast<const T*>(data);
}
// Генерирует исключение BadOptionalAccess, если объекта нет
T& Value() {
if (!defined) {
throw BadOptionalAccess{};
}
return *reinterpret_cast<T*>(data);
}
const T& Value() const {
if (!defined) {
throw BadOptionalAccess{};
}
return *reinterpret_cast<const T*>(data);
}
void Reset() {
if (!defined)
return;
defined = false;
T* data_ptr = reinterpret_cast<T*>(data);
if (data_ptr != nullptr) {
data_ptr->~T();
}
//delete [] data;
}
~Optional() {
Reset();
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment