我想在结构中存储std::istream
,并能够从另一个流构建它。目前,我有以下内容:
struct A {
A(std::istream &in) : in_(in.rdbuf()) { }
protected:
std::istream in_;
};
这只允许从已创建的对象创建类型为A
的对象,但我希望能够执行以下操作:
A(std::stringstream("hello world!"));
我为A
的rvalue-reference添加了std::istream
的以下重载:
A(std::istream &&in) : in_(in.rdbuf()) {
in.rdbuf(nullptr); // (1)
}
我添加了(1)
以避免在序列点之后销毁缓冲区,但我不知道这种行为是否已定义或者这是否真的有意义?
我可以根据标准“移动”这样的流缓冲区吗?如果没有,这是否适用于标准实施和流(std::stringstream
,std::fstream
,...)?
答案 0 :(得分:1)
最好避免处理缓冲区并让对象在内部为我们工作。
我提出以下建议。使用模板,当我们将流移动到结构中时,我们可以确保构造完整类型的共享指针。
#include <sstream>
#include <iostream>
#include <fstream>
#include <memory>
#include <typeinfo>
struct A {
template<typename T>
A(T& is) : is{is}
{
std::cout << "Only taking a reference. Not taking ownership of std::istream.\n";
}
template<typename T>
A(T&& is) : own_is{std::make_unique<T>(std::move(is))}, is{*own_is}
{
std::cout << "using move. Not sliced.\n";
}
void print_stream_type()
{
std::cout << typeid(is).name() << '\n';
}
protected:
std::unique_ptr<std::istream> own_is;
std::istream& is;
};
int main()
{
A a1{std::stringstream{"hello world!"}};
A a2{std::cin};
std::ifstream ifs{"input.txt"};
A a3{ifs};
A a4{std::ifstream{"input.txt"}};
a1.print_stream_type();
a2.print_stream_type();
a3.print_stream_type();
a4.print_stream_type();
}
MSVC2017上的输出:
using move. Not sliced.
Only taking a reference. Not taking ownership of std::istream.
Only taking a reference. Not taking ownership of std::istream.
using move. Not sliced.
class std::basic_stringstream<char,struct std::char_traits<char>,class std::allocator<char> >
class std::basic_istream<char,struct std::char_traits<char> >
class std::basic_ifstream<char,struct std::char_traits<char> >
class std::basic_ifstream<char,struct std::char_traits<char> >
另请注意结构中成员的顺序很重要。如果own_is
是通过引用构造的,则A
变量将为null。但是如果我们传递一个rvalue,那么它将首先创建共享指针,然后取消引用它以传递有效的引用。在这两种情况下,相同的参考点都指向流。