繁体   English   中英

麻烦包装std :: istream,unique_ptr在移动后不为null

[英]Trouble wrapping std::istream, unique_ptr not null after moved

由于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不为null的奇怪现象。

我正在使用MSVC编译器btw。

你不能理所当然地做你想做的事。

据我所知,你想要一些尽可能与::std::istream无法区分的对象,但是它具有移动语义,以便在不再需要它之后自动销毁它。

可以被称为::std::istream的任何东西的主要有用属性是它是从::std::istream派生的,因为它是::std::istream ,它不能移动,没有从中衍生出来也可以移动。

所以,你留下了下一个最好的东西, ::std::unique_ptr到你的::std::istream 这意味着你将不得不一直使用* ,这将是丑陋的。 那好吧。

没有多少聪明可以让你创建任何类型的同时可移动的包装器,并从::std::istream派生,这样它就可以使用所有期望的好的现有库函数。 你也不能做出任何合理的行为,就像参考而没有那个东西实际上是一个参考。

顺便说一句,当你构造一个unique_ptr你可以提供一个自定义删除器,如果你实际上指向你不想删除的::std::cin类的东西。 在这种情况下,您可以使自定义删除器完全不执行任何操作。 移动unique_ptr时,自定义删除器将与指针一起向右移动。

此函数返回悬空引用:

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

函数返回时会销毁临时对象。 而是将函数更改为按值返回, IStream wrap() {

此外, shared_(*owned_)导致悬空引用,因为它引用当前拥有的对象所在的内存位置,即使该对象被销毁并且unique_ptr稍后更改为拥有不同的对象。

摆脱shared_是个好主意,只需根据需要调用owned_.get()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM