简体   繁体   中英

Trouble wrapping std::istream, unique_ptr not null after moved

Since std::istream can't be moved (protected function), I was trying to wrap the std::istream so that I can build my code upon customized stream factory.

So far I have tried inheriting directly from std::istream like this:

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);
  }
};

But it causes a segmentation fault (any insights on the reason would be appreciated), so I move on to some "safer-looking" method.

Here is the code I currently use:

#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;
}

Sad thing is this code still won't work, and I found that assertion at IStream::IStream(IStream&&) failed.

Output:

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

Which leads to wierd phenomenon where unique_ptr is not null after moved.

I am using MSVC compiler btw.

You cannot sanely do what you want.

As I understand it, you want some sort of object that is as indistinguishable from a ::std::istream as possible, but has move semantics so that it will be automatically destroyed after it's no longer needed.

The primary useful attribute of anything that can be referred to as an ::std::istream is that it is derived from ::std::istream , and since it is ::std::istream that can't be moved, nothing derived from it can be moved either.

So, you're left with the next best thing, a ::std::unique_ptr to your ::std::istream thing. This means you'll have to use * all the time and it will be ugly. Oh, well.

No amount of cleverness will allow you to create any kind of wrapper that is simultaneously moveable, and derived from ::std::istream so that it works with all the nice existing library functions that expect that. Nor can you make something that acts in any reasonable way like a reference without that thing actually being a reference.

BTW, when you construct a unique_ptr you can provide a custom deleter if you're actually pointing at something like ::std::cin that you don't want to delete. You can make the custom deleter simply do nothing in that case. The custom deleter will be moved right along with the pointer when you move your unique_ptr around.

This function returns a dangling reference:

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

The temporary object is destroyed when the function returns. Instead change the function to return by value, IStream wrap() { .

Also, shared_(*owned_) leads to a dangling reference because that refers to the memory location where the currently-owned object resides, even if that object is destroyed and the unique_ptr is later changed to own a different object.

It would be a good idea to get rid of shared_ , and just call owned_.get() as needed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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