Устранение неполадок std :: istream, unique_ptr не равно NULL после перемещения - PullRequest
3 голосов
/ 20 мая 2019

Поскольку std :: istream не может быть перемещен (защищенная функция), я пытался обернуть std :: istream, чтобы я мог построить свой код на настроенной фабрике потоков.

Пока что у меня естьпопытался наследовать напрямую от std :: istream следующим образом:

class IStream : public std::istream{
public:
  // template <typename T>
  IStream(std::istringstream&& rhs) : std::istream(std::move(rhs)){
    this->rdbuf(rhs.rdbuf());
    rhs.istream::rdbuf(NULL);
  }
  IStream(IStream&& rhs) : std::istream(std::move(rhs)){
    this->rdbuf(rhs.rdbuf());
    rhs.rdbuf(NULL);
  }
};

Но это вызывает ошибку сегментации (любые соображения по поводу причины приветствуются), поэтому я перехожу к некоторому «более безопасному» методу.

Вот код, который я сейчас использую:

#include <iostream>
#include <sstream>
#include <istream>
#include <string>
#include <memory>
#include <cassert>

using namespace std;

class IStream {
 public:
    IStream(const IStream& rhs) = delete;
  template <typename T>
  IStream(T& rhs) : shared_(rhs) {
    std::cout << "called IStream(T&)" << std::endl;
  }
  // assume strict order between member construct for now
  template <typename T>
  IStream(T&& rhs) : owned_{std::make_unique<T>(std::move(rhs))}, shared_(*owned_) {
    std::cout << "called IStream(T&&)" << std::endl;
  }
  IStream(IStream&& rhs) : owned_(std::move(rhs.owned_)), shared_(*owned_) {
    assert(rhs.owned_.get() == nullptr); // failed
    std::cout << "called IStream(IStream&&)" << std::endl;
  }
  std::istream& get() {
    return shared_;
  }
  ~IStream() {
    std::cout << "called ~IStream with " << (owned_.get()!=nullptr) << std::endl;
  }
 private:
  std::unique_ptr<std::istream> owned_;
  std::istream& shared_;
};

IStream&& wrap() {
    return IStream(istringstream{"test"});
}

int main(void) {

    IStream is(wrap());
    char buf[10];
    memset(buf, 0, sizeof(char) * 10);
    is.get().getline(buf, 10);
    std::cout << std::string(buf) << std::endl; 

    return 0;
}

Печально, но этот код по-прежнему не работает, и я обнаружил, что утверждение на IStream::IStream(IStream&&) не удалось.

Вывод:

called IStream(T&&)
called ~IStream with 1
Assertion failed: rhs.owned_.get() == nullptr, file .\tmp.cpp, line 23

Что приводит к странному явлению, когда unique_ptr не является нулевым после перемещения.

Я использую компилятор MSVC, кстати.

Ответы [ 2 ]

3 голосов
/ 20 мая 2019

Вы не можете разумно делать то, что хотите.

Насколько я понимаю, вам нужен какой-то объект, который настолько неотличим от ::std::istream, насколько это возможно, но имеет семантику перемещения, так что он будет автоматически уничтожен после того, как он больше не нужен.

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

Итак, у вас есть следующая лучшая вещь, ::std::unique_ptr для вашей ::std::istream вещи. Это означает, что вам придется использовать * все время, и это будет ужасно. Ох, хорошо.

Никакая хитрость не позволит вам создать какую-либо оболочку, которая будет одновременно перемещаемой и полученной из ::std::istream, чтобы она работала со всеми существующими библиотечными функциями, которые этого ожидают. Вы также не можете сделать что-то, что действует каким-либо разумным образом, как ссылка, но эта вещь на самом деле не является ссылкой.

Кстати, когда вы создаете unique_ptr, вы можете предоставить пользовательское средство удаления, если вы на самом деле указываете на что-то вроде ::std::cin, которое вы не хотите удалять. Вы можете заставить пользовательское средство удаления просто ничего не делать в этом случае. Пользовательский инструмент удаления будет перемещен вместе с указателем при перемещении unique_ptr вокруг.

2 голосов
/ 20 мая 2019

Эта функция возвращает висячую ссылку:

IStream&& wrap() {
    return IStream(istringstream{"test"});
}

Временный объект уничтожается, когда функция возвращается.Вместо этого измените функцию на возвращаемую по значению, IStream wrap() {.

Кроме того, shared_(*owned_) приводит к зависанию ссылки, поскольку это относится к области памяти, где находится принадлежащий в настоящее время объект, даже если этот объект уничтожени unique_ptr позже изменяется на собственный другой объект.

Было бы неплохо избавиться от shared_ и просто позвонить owned_.get() при необходимости.

...