由于无法移动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编译器。
答案 0 :(得分:3)
你不能理智地做你想做的事。
据我了解,您想要某种与::std::istream
尽可能不可区分的对象,但具有移动语义,以便在不再需要它时将其自动销毁。
任何可以称为::std::istream
的东西的主要有用属性是它是从::std::istream
派生的,并且由于它是::std::istream
不能被移动,所以什么也没有从它派生的也可以移动。
因此,您剩下的第二件事是,::std::unique_ptr
比::std::istream
好。这意味着您必须一直使用*
,而且很难看。哦,很好。
没有什么聪明的方法可让您创建可同时移动并从{{1}}派生的任何类型的包装器,以便它可以与所有期望的现有库函数一起使用。您也无法做出以任何合理方式行事的事物,例如参考文献,而实际上没有作为参考文献。
顺便说一句,构造::std::istream
时,如果您实际上要指向不想删除的unique_ptr
之类的对象,则可以提供自定义删除器。在这种情况下,您可以使自定义删除器完全不执行任何操作。当您移动::std::cin
时,自定义删除器将随指针一起向右移动。
答案 1 :(得分:2)
此函数返回一个悬空的引用:
IStream&& wrap() {
return IStream(istringstream{"test"});
}
函数返回时,临时对象将被销毁。而是将函数更改为按值IStream wrap() {
返回。
此外,shared_(*owned_)
导致悬空引用,因为它引用了当前拥有的对象所在的内存位置,即使该对象被销毁并且后来将unique_ptr更改为拥有另一个对象也是如此。
摆脱shared_
,并根据需要调用owned_.get()
是个好主意。