[英]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.