因为它是按设计的 std::ostream
无法移动 问题变成:怎么可能 std::ostream
被移动,以便它可以写入不同的目的地?
基本目标是让工厂函数获取URI并返回一些内容,让我们称之为, omstream
(输出可移动流),可以像 std::ostream
:
omstream stream_factory(std::string const& uri);
void process(std::ostream& out);
int main(int ac, char* av[]) {
omstream destination{ stream_factory(ac == 2? av[1]: "example.txt") };
process(destination);
}
该 omstream
将负责正确移动对象:
class omstream
: public std::ostream {
// suitable members
public:
omstream(/* suitable constructor arguments */);
omstream(omstream&& other) // follow recipe of 27.9.1.11 [ofstream.cons] paragraph 4
: std:ios(std::move(other))
, std::ostream(std::move(other))
// move any members {
this->set_rdbuf(/* get the stream buffer */);
}
// other helpful or necessary members
};
问题实际上是实施的 omstream
(或者,甚至是相应的类模板 basic_omstream
)?
你几乎做对了。你的例子是移动构建 ios
基地两次。你应该搬家 只要 该 直接 基类。并假设有会员 streambuf
,移动它:
class omstream
: public std::ostream {
// suitable members
public:
omstream(/* suitable constructor arguments */);
omstream(omstream&& other) // follow recipe of 27.9.1.11 [ofstream.cons] paragraph 4
: std: ostream(std::move(other)),
// move any members {
this->set_rdbuf(/* install the stream buffer */);
}
// other helpful or necessary members
};
我把“get”更改为“install” set_rdbuf
评论。通常,这会安装指向该成员的指针 streambuf
进入 ios
基类。
目前非正统的设计移动和交换成员 istream/ostream
设置为移动和交换派生类的成员(例如 ofstream
和 omstream
)更直观。食谱是:
移动基础和成员,并在移动构造函数中设置 rdbuf
。
它是嵌入式的 rdbuf
这是整个层次结构的复杂因素。
霍华德答案中公布的代码是草稿(基于问题中的草案)。霍华德的回答帮助解决了令人困惑的问题 virtual
基类 std::ios
:当将派生流作为移动流时,需要默认构造基类 std::ios
流的一部分将被显式移动 std::ostream
移动构造函数使用 std::ios::move()
。这个答案只是填补了缺失的部分。
下面的实现维护了一个指向流缓冲区的指针,该缓冲区通常预期存在于堆上,并在破坏时将被释放 std::unique_ptr<...>
。因为可能需要返回 std::omstream
长寿命流的流缓冲区,例如, std::cout
, std::unique_ptr<...>
设置为使用删除器,如果 omstream
不拥有流缓冲区。
#include <ostream>
#include <memory>
#include <utility>
template <typename cT, typename Traits = std::char_traits<cT>>
class basic_omstream
: public std::basic_ostream<cT, Traits>
{
using deleter = void (*)(std::basic_streambuf<cT, Traits>*);
static void delete_sbuf(std::basic_streambuf<cT, Traits>* sbuf) {
delete sbuf;
}
static void ignore_sbuf(std::basic_streambuf<cT, Traits>*) {
}
std::unique_ptr<std::basic_streambuf<cT, Traits>, deleter> m_sbuf;
public:
basic_omstream()
: std::basic_ios<cT, Traits>()
, std::basic_ostream<cT, Traits>(nullptr)
, m_sbuf(nullptr, &ignore_sbuf) {
}
basic_omstream(std::basic_streambuf<cT, Traits>* sbuf,
bool owns_streambuf)
: std::basic_ios<cT, Traits>()
, std::basic_ostream<cT, Traits>(sbuf)
, m_sbuf(sbuf, owns_streambuf? &delete_sbuf: &ignore_sbuf) {
this->set_rdbuf(this->m_sbuf.get());
}
basic_omstream(basic_omstream&& other)
: std::basic_ios<cT, Traits>() // default construct ios!
, std::basic_ostream<cT, Traits>(std::move(other))
, m_sbuf(std::move(other.m_sbuf)) {
this->set_rdbuf(this->m_sbuf.get());
}
basic_omstream& operator=(basic_omstream&& other) {
this->std::basic_ostream<cT, Traits>::swap(other);
this->m_sbuf.swap(other.m_sbuf);
this->set_rdbuf(this->m_sbuf.get());
return *this;
}
};
typedef basic_omstream<char> omstream;
typedef basic_omstream<wchar_t> womstream;
用一个 std::ofstream
或者 std::ostringstream
初始化一个 omstream
除非相应的流超过了,否则不起作用 omstream
。通常,将分配相应的流缓冲区。班上 omstream
例如,可以像下面的代码一样使用,该代码基于给予合适的工厂函数的URI创建流:
#include <iostream>
#include <sstream>
#include <fstream>
omstream make_stream(std::string const& uri) {
if (uri == "stream://stdout") {
return omstream(std::cout.rdbuf(), false);
}
else if (uri == "stream://stdlog") {
return omstream(std::clog.rdbuf(), false);
}
else if (uri == "stream://stderr") {
return omstream(std::cerr.rdbuf(), false);
}
else if (uri.substr(0, 8) == "file:///") {
std::unique_ptr<std::filebuf> fbuf(new std::filebuf);
fbuf->open(uri.substr(8), std::ios_base::out);
return omstream(fbuf.release(), true);
}
else if (uri.substr(0, 9) == "string://") {
return omstream(new std::stringbuf(uri.substr(9)), true);
}
throw std::runtime_error("unknown URI: '" + uri + "'");
}
int main(int ac, char* av[])
{
omstream out{ make_stream(ac == 2? av[1]: "stream://stdout") };
out << "hello, world\n";
}
如果有其他可用的流缓冲区可以从URI构造,则可以将这些缓冲区添加到 make_stream()
功能。