Невозможно разыменовать указатель - PullRequest
2 голосов
/ 27 марта 2019

Я пишу реализацию Haskell Maybe Monad в C ++ 11.

Однако я застрял, когда попытался проверить код.Когда я устанавливаю значение типа с помощью псевдо-конструктора Just, а затем пытаюсь оценить его с помощью функции fromJust (которая должна просто "распаковать" значение, помещенное в Maybe), программа останавливается и в конечном итоге завершает работу без вывода сообщений.

Итак, я попытался отладить его;Вот вывод для кода testMaybe.cpp:

c1
yeih2
value not null: 0xf16e70

Я добавил пару операторов print, чтобы оценить, где программа останавливается, и кажется, что она останавливается в той точке, где я разыменовываю указатель навернуть значение.(Я пометил это в коде.)

Сначала я подумал, что значение в Maybe может быть деконструировано к тому времени, когда я хочу разыменовать указатель, что, на мой взгляд, приведет к неопределенностиповедение или прекращение.Однако я не смог найти место, где это могло бы произойти.

Не могли бы вы дать мне подсказку, почему это происходит?

testMaybe.cpp:

#include<iostream>
#include "Maybe.hpp"
using namespace std;
using namespace Functional_Maybe;
int main() {
        Maybe<string> a{Maybe<string>::Just("hello") };
        if(!isNothing(a)) cout << "yeih2 " << fromJust(a) << endl;
        return 0;
}

Maybe.hpp

#pragma once
#include<stdexcept>
#include<iostream>
using namespace std;
namespace Functional_Maybe {

  template <typename T>
  class Maybe {
    const T* value;

    public:
          Maybe(T *v) : value { v } {}            //public for return in join
          const static Maybe<T> nothing;

          static Maybe<T> Just (const T &v) { cout << "c1" << endl; return Maybe<T> { new T(v) }; }

          T fromJust() const {
                  if (isNothing()) throw std::runtime_error("Tried to extract value from Nothing");
                  cout << "\nvalue not null: " << value << " " << *value << endl;
                                        //                        ^ stops here
                  return *value;
          }

          bool isNothing() const { return value==nullptr; }

          ~Maybe() { if (value != nullptr) delete value; }
  };

  template <typename T>
  bool isNothing(Maybe<T> val) {
    return val.isNothing();
  }

  template <typename T>
  T fromJust(Maybe<T> val) {
    return val.fromJust();
  }
}

1 Ответ

4 голосов
/ 27 марта 2019

Шаблон класса Maybe владеет ресурсами (динамически распределяемым T), но не следует правилу Three : (неявно определенные) операции копирования и перемещения выполняют только полные копии, которыеприводит к проблемам использования после освобождения и двойного освобождения.Вы должны либо реализовать правильные операции копирования и перемещения (cosntructors и операторы присваивания) для вашего класса, либо использовать std::unique_ptr<const T> в качестве типа value, и удалить свой ручной деструктор (тем самым следуя предпочтительному правилу нуля).

Примечание: вы рассматривали std::optional (или, в версиях до C ++ 17, boost::optional)?Похоже, что они делают что-то очень похожее (или даже идентичное) вашему предлагаемому классу, и вы можете использовать их вместо этого (или использовать их внутри вашего класса, если это вам подходит).Они могут быть даже более эффективными, используя оптимизацию небольших объектов, чтобы в некоторых случаях избежать динамического выделения памяти.

...