Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/142.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何移动std::ostream?_C++_C++11_Iostream - Fatal编程技术网

C++ 如何移动std::ostream?

C++ 如何移动std::ostream?,c++,c++11,iostream,C++,C++11,Iostream,由于设计的原因,问题变成了:如何移动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

由于设计的原因,问题变成了:如何移动
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\u omstream
)需要什么?

您几乎做到了。您的示例是移动两次构建
ios
base。您应该只移动direct基类。假设存在成员
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
};
我在
set\rdbuf
注释中将“get”改为“install”。通常,这会将指向成员
streambuf
的指针安装到
ios
基类中

istream/ostream
的移动和交换成员的当前非常规设计是为了使派生类(如
of stream
omstream
)的移动和交换成员更直观。配方是:

移动基和成员,并在移动构造函数中设置
rdbuf


正是嵌入的
rdbuf
是整个层次结构的复杂因素。

霍华德答案中的代码是草稿(基于问题中的草稿)。Howard的回答帮助解决了
virtual
基类
std::ios
:基类在移动派生流时需要默认构造,因为流的
std::ios
部分将由
std::ostream
move构造函数使用
std::ios::move()显式移动
。这个答案只是填补了缺失的部分

下面的实现维护了一个指向流缓冲区的指针,该缓冲区通常位于堆上,并在销毁时在
std::unique\u 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;

如果有其他可以从URI构造的流缓冲区,可以将它们添加到
make_stream()
函数中。

如果我错了,请原谅,对右值引用调用
std::move
不是有点…多余吗?像这样@克丽丝:你是说
std::move(其他)
?当一个对象有名字的时候,它就不是右值了。在参数列表中,它基本上表示可以将右值传递给函数,但在函数中,参数是左值?(从未见过它是含蓄地写的……)第一句话是o型的吗?链接的问题说,
ostream
是不可移动的。它并没有说流的
是不可移动的<流的code>是可移动的。我完全错过了
std::basic\u ostream
的move构造函数调用
std::basic\u ios::move()
。也就是说,对
std::basic_ios
默认构造函数的调用实际上不是从
std::basic_ostream
执行的,而是从派生最多的类型执行的,因为它是一个
virtual
base:27.7.3.2[ostream.cons]中的语句调用
std::basic_ios
的默认构造函数的第5段是派生流契约的一部分!这是一个令人困惑的领域。每次看它我都要重新学习。
#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";
}