我想为ostream实现一个自定义操纵器,以对插入到流中的下一个项目进行一些操作。例如,假设我有一个自定义操纵符:

std::ostringstream os;
std::string name("Joe");
os << "SELECT * FROM customers WHERE name = " << quote << name;

机械手引用将引用名称以产生:
SELECT * FROM customers WHERE name = 'Joe'

我该如何去实现呢?
谢谢。

最佳答案

将操纵器添加到C++流特别困难,因为无法控制操纵器的使用方式。可以将新的语言环境注入(inject)到流中,该流中已安装了一个构面,该构面控制数字的打印方式,但不能控制字符串的输出方式。然后问题仍然是如何将报价状态安全地存储到流中。

使用std命名空间中定义的运算符输出字符串。如果要更改打印方式,同时又保持机械手的外观,则可以创建一个代理类:

namespace quoting {
struct quoting_proxy {
    explicit quoting_proxy(std::ostream & os):os(os){}

    template<typename Rhs>
    friend std::ostream & operator<<(quoting_proxy const& q,
                                     Rhs const& rhs) {
        return q.os << rhs;
    }

    friend std::ostream & operator<<(quoting_proxy const& q,
                                     std::string const& rhs) {
        return q.os << "'" << rhs << "'";
    }

    friend std::ostream & operator<<(quoting_proxy const& q,
                                     char const* rhs) {
        return q.os << "'" << rhs << "'";
    }
private:
    std::ostream & os;
};

struct quoting_creator { } quote;
quoting_proxy operator<<(std::ostream & os, quoting_creator) {
    return quoting_proxy(os);
}
}

int main() {
    std::cout << quoting::quote << "hello" << std::endl;
}

哪一个适合用于ostream。如果要一概而论,也可以使其成为模板,并且也可以接受basic_stream而不是plain string。在某些情况下,它与标准操纵器具有不同的行为。由于它通过返回代理对象起作用,因此在诸如以下情况下将不起作用
std::cout << quoting::quote;
std::cout << "hello";

09-04 11:18