Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // main.cpp
- #include "optional.h"
- #include <cassert>
- struct C {
- C() noexcept {
- ++def_ctor;
- }
- C(const C& /*other*/) noexcept {
- ++copy_ctor;
- }
- C(C&& /*other*/) noexcept {
- ++move_ctor;
- }
- C& operator=(const C& other) noexcept {
- if (this != &other) {
- ++copy_assign;
- }
- return *this;
- }
- C& operator=(C&& /*other*/) noexcept {
- ++move_assign;
- return *this;
- }
- ~C() {
- ++dtor;
- }
- static size_t InstanceCount() {
- return def_ctor + copy_ctor + move_ctor - dtor;
- }
- static void Reset() {
- def_ctor = 0;
- copy_ctor = 0;
- move_ctor = 0;
- copy_assign = 0;
- move_assign = 0;
- dtor = 0;
- }
- inline static size_t def_ctor = 0;
- inline static size_t copy_ctor = 0;
- inline static size_t move_ctor = 0;
- inline static size_t copy_assign = 0;
- inline static size_t move_assign = 0;
- inline static size_t dtor = 0;
- };
- void TestInitialization() {
- C::Reset();
- {
- Optional<C> o;
- assert(!o.HasValue());
- assert(C::InstanceCount() == 0);
- }
- assert(C::InstanceCount() == 0);
- C::Reset();
- {
- C c;
- Optional<C> o(c);
- assert(o.HasValue());
- assert(C::def_ctor == 1 && C::copy_ctor == 1);
- assert(C::InstanceCount() == 2);
- }
- assert(C::InstanceCount() == 0);
- C::Reset();
- {
- C c;
- Optional<C> o(std::move(c));
- assert(o.HasValue());
- assert(C::def_ctor == 1 && C::move_ctor == 1 && C::copy_ctor == 0 && C::copy_assign == 0
- && C::move_assign == 0);
- assert(C::InstanceCount() == 2);
- }
- assert(C::InstanceCount() == 0);
- C::Reset();
- {
- C c;
- Optional<C> o1(c);
- const Optional<C> o2(o1);
- assert(o1.HasValue());
- assert(o2.HasValue());
- assert(C::def_ctor == 1 && C::move_ctor == 0 && C::copy_ctor == 2 && C::copy_assign == 0
- && C::move_assign == 0);
- assert(C::InstanceCount() == 3);
- }
- assert(C::InstanceCount() == 0);
- C::Reset();
- {
- C c;
- Optional<C> o1(c);
- const Optional<C> o2(std::move(o1));
- assert(C::def_ctor == 1 && C::copy_ctor == 1 && C::move_ctor == 1 && C::copy_assign == 0
- && C::move_assign == 0);
- assert(C::InstanceCount() == 3);
- }
- assert(C::InstanceCount() == 0);
- }
- void TestAssignment() {
- Optional<C> o1;
- Optional<C> o2;
- { // Assign a value to empty
- C::Reset();
- C c;
- o1 = c;
- assert(C::def_ctor == 1 && C::copy_ctor == 1 && C::dtor == 0);
- }
- { // Assign a non-empty to empty
- C::Reset();
- o2 = o1;
- assert(C::copy_ctor == 1 && C::copy_assign == 0 && C::dtor == 0);
- }
- { // Assign non empty to non-empty
- C::Reset();
- o2 = o1;
- assert(C::copy_ctor == 0 && C::copy_assign == 1 && C::dtor == 0);
- }
- { // Assign empty to non empty
- C::Reset();
- Optional<C> empty;
- o1 = empty;
- assert(C::copy_ctor == 0 && C::dtor == 1);
- assert(!o1.HasValue());
- }
- }
- void TestMoveAssignment() {
- { // Assign a value to empty
- Optional<C> o1;
- C::Reset();
- C c;
- o1 = std::move(c);
- assert(C::def_ctor == 1 && C::move_ctor == 1 && C::dtor == 0);
- }
- { // Assign a non-empty to empty
- Optional<C> o1;
- Optional<C> o2{C{}};
- C::Reset();
- o1 = std::move(o2);
- assert(C::move_ctor == 1 && C::move_assign == 0 && C::dtor == 0);
- }
- { // Assign non empty to non-empty
- Optional<C> o1{C{}};
- Optional<C> o2{C{}};
- C::Reset();
- o2 = std::move(o1);
- assert(C::copy_ctor == 0 && C::move_assign == 1 && C::dtor == 0);
- }
- { // Assign empty to non empty
- Optional<C> o1{C{}};
- C::Reset();
- Optional<C> empty;
- o1 = std::move(empty);
- assert(C::copy_ctor == 0 && C::move_ctor == 0 && C::move_assign == 0 && C::dtor == 1);
- assert(!o1.HasValue());
- }
- }
- void TestValueAccess() {
- using namespace std::literals;
- {
- Optional<std::string> o;
- o = "hello"s;
- assert(o.HasValue());
- assert(o.Value() == "hello"s);
- assert(&*o == &o.Value());
- assert(o->length() == 5);
- }
- {
- try {
- Optional<int> o;
- [[maybe_unused]] int v = o.Value();
- assert(false);
- } catch (const BadOptionalAccess& /*e*/) {
- } catch (...) {
- assert(false);
- }
- }
- }
- void TestReset() {
- C::Reset();
- {
- Optional<C> o{C()};
- assert(o.HasValue());
- o.Reset();
- assert(!o.HasValue());
- }
- }
- int main() {
- try {
- TestInitialization();
- TestAssignment();
- TestMoveAssignment();
- TestValueAccess();
- TestReset();
- } catch (...) {
- assert(false);
- }
- }
- // optional.h
- #include <stdexcept>
- #include <utility>
- // Исключение этого типа должно генерироватся при обращении к пустому optional
- class BadOptionalAccess : public std::exception {
- public:
- using exception::exception;
- virtual const char* what() const noexcept override {
- return "Bad optional access";
- }
- };
- template <typename T>
- class Optional {
- public:
- Optional() = default;
- Optional(const T& value);
- Optional(T&& value);
- Optional(const Optional& other);
- Optional(Optional&& other);
- Optional& operator=(const T& value);
- Optional& operator=(T&& rhs);
- Optional& operator=(const Optional& rhs);
- Optional& operator=(Optional&& rhs);
- ~Optional();
- bool HasValue() const;
- // Операторы * и -> не должны делать никаких проверок на пустоту Optional.
- // Эти проверки остаются на совести программиста
- T& operator*();
- const T& operator*() const;
- T* operator->();
- const T* operator->() const;
- // Метод Value() генерирует исключение BadOptionalAccess, если Optional пуст
- T& Value();
- const T& Value() const;
- void Reset();
- private:
- // alignas нужен для правильного выравнивания блока памяти
- alignas(T) char data_[sizeof(T)];
- T* ptr_ = nullptr;
- bool is_initialized_ = false;
- };
- template <typename T>
- Optional<T>::Optional(const T& value)
- : is_initialized_(true) {
- ptr_ = new(&data_[0]) T(value);
- }
- template <typename T>
- Optional<T>::Optional(T&& value)
- : is_initialized_(true) {
- ptr_ = new(&data_[0]) T(std::move(value));
- }
- template <typename T>
- Optional<T>::Optional(const Optional& other)
- : is_initialized_(other.is_initialized_) {
- if (is_initialized_) {
- ptr_ = new(&data_[0]) T(other.Value());
- }
- }
- template <typename T>
- Optional<T>::Optional(Optional&& other)
- : is_initialized_(std::move(other.is_initialized_)) {
- if (is_initialized_) {
- ptr_ = new(&data_[0]) T(std::move(other.Value()));
- }
- }
- template <typename T>
- Optional<T>::~Optional() {
- Reset();
- }
- template <typename T>
- Optional<T>& Optional<T>::operator=(const T& value) {
- if (!is_initialized_) {
- ptr_ = new(&data_[0]) T(value);
- is_initialized_ = true;
- }
- else {
- *ptr_ = value;
- }
- return *this;
- }
- template <typename T>
- Optional<T>& Optional<T>::operator=(T&& rhs) {
- if (!is_initialized_) {
- ptr_ = new(&data_[0]) T(std::move(rhs));
- is_initialized_ = true;
- }
- else {
- *ptr_ = std::move(rhs);
- }
- return *this;
- }
- template <typename T>
- Optional<T>& Optional<T>::operator=(const Optional& rhs) {
- if (!is_initialized_) {
- if (rhs.is_initialized_) {
- ptr_ = new(&data_[0]) T(rhs.Value());
- is_initialized_ = rhs.is_initialized_;
- }
- }
- else {
- if (rhs.is_initialized_) {
- *ptr_ = rhs.Value();
- }
- else {
- Reset();
- }
- }
- return *this;
- }
- template <typename T>
- Optional<T>& Optional<T>::operator=(Optional&& rhs) {
- if (!is_initialized_) {
- if (rhs.is_initialized_) {
- ptr_ = new(&data_[0]) T(std::move(rhs.Value()));
- is_initialized_ = std::move(rhs.is_initialized_);
- }
- }
- else {
- if (rhs.is_initialized_) {
- *ptr_ = std::move(rhs.Value());
- }
- else {
- Reset();
- }
- }
- return *this;
- }
- template <typename T>
- bool Optional<T>::HasValue() const {
- return is_initialized_;
- }
- template <typename T>
- T& Optional<T>::operator*() {
- return *ptr_;
- }
- template <typename T>
- const T& Optional<T>::operator*() const {
- return *ptr_;
- }
- template <typename T>
- T* Optional<T>::operator->() {
- return ptr_;
- }
- template <typename T>
- const T* Optional<T>::operator->() const {
- return ptr_;
- }
- template <typename T>
- T& Optional<T>::Value() {
- if (!is_initialized_) {
- throw BadOptionalAccess();
- }
- return *ptr_;
- }
- template <typename T>
- const T& Optional<T>::Value() const {
- if (!is_initialized_) {
- throw BadOptionalAccess();
- }
- return *ptr_;
- }
- template <typename T>
- void Optional<T>::Reset() {
- if (is_initialized_) {
- ptr_->~T();
- ptr_ = nullptr;
- }
- is_initialized_ = false;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement