[英]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. 由于std :: istream无法移动(受保护的函数),我试图包装std :: istream,以便我可以在自定义流工厂上构建我的代码。
So far I have tried inheriting directly from std::istream like this: 到目前为止,我尝试直接从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);
}
};
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. 可悲的是这段代码仍然不起作用,我发现
IStream::IStream(IStream&&)
断言失败了。
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. 这导致了移动后unique_ptr不为null的奇怪现象。
I am using MSVC compiler btw. 我正在使用MSVC编译器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. 据我所知,你想要一些尽可能与
::std::istream
无法区分的对象,但是它具有移动语义,以便在不再需要它之后自动销毁它。
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. 可以被称为
::std::istream
的任何东西的主要有用属性是它是从::std::istream
派生的,因为它是::std::istream
,它不能移动,没有从中衍生出来也可以移动。
So, you're left with the next best thing, a ::std::unique_ptr
to your ::std::istream
thing. 所以,你留下了下一个最好的东西,
::std::unique_ptr
到你的::std::istream
。 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. 没有多少聪明可以让你创建任何类型的同时可移动的包装器,并从
::std::istream
派生,这样它就可以使用所有期望的好的现有库函数。 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. 顺便说一句,当你构造一个
unique_ptr
你可以提供一个自定义删除器,如果你实际上指向你不想删除的::std::cin
类的东西。 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. 移动
unique_ptr
时,自定义删除器将与指针一起向右移动。
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() {
. 而是将函数更改为按值返回,
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. 此外,
shared_(*owned_)
导致悬空引用,因为它引用当前拥有的对象所在的内存位置,即使该对象被销毁并且unique_ptr稍后更改为拥有不同的对象。
It would be a good idea to get rid of shared_
, and just call owned_.get()
as needed. 摆脱
shared_
是个好主意,只需根据需要调用owned_.get()
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.