【问题标题】:How to override ostream << operator for already defined type如何覆盖已定义类型的 ostream << 运算符
【发布时间】:2013-06-12 12:11:22
【问题描述】:

我目前正在开发一个现有库的端口,该库利用 ostream 写入终端。

ostream 是作为端口的一部分派生的。

使用的ostream派生类定义如下:

class owstream: public std::ostream {
public:
    CTerminal * output;
    giac::context * contextptr;
    owstream(CTerminal *, giac::context * contextptr , int = 0);
    virtual ~owstream();
};

这用于输出一些数据,通常是整数和双精度数。

问题是我正在使用的平台有一个错误的双打印例程,导致内核崩溃。

所以在某些情况下,如果我这样做:

ostream* mystream = new ostream(...);
(*mystream) << 1.23456

轰轰烈烈

所以,我试图覆盖特定类型的

ostream* GetStream() { return = new MyOStream(...); }

....

ostream* mystream = GetStream()
mystream << 1.23456;

不幸的是,ostream 中的 operator

我试图用类似的东西来扩展它:

class owstream: public std::ostream {
    friend std::ostream& operator<<(std::ostream& out, double val);
  public:
    CTerminal * output;
    giac::context * contextptr;
    owstream(CTerminal *, giac::context * contextptr , int = 0);
    virtual ~owstream();
};

extern std::ostream& operator<<(std::ostream &out, double val);

但这会导致关于运算符

我开始怀疑这是否可能。

当给定一个特定的、已经处理过的类型时,你将如何实现

能够做到以下几点的目标:

cout << 1.234567

不会崩溃(我显然不会使用 cout,但上面定义的 GetStream() 可以很好地返回 cout)

【问题讨论】:

    标签: c++ inheritance overriding


    【解决方案1】:

    我发现您当前的方法/实施存在三个问题。

    问题 #1

    即使您成功地让它使用std::ostream 作为第一个参数并返回值,它也无法正常工作。问题主要源于从重载的operator&lt;&lt; 返回一个ostream。在第一个值被发送到owstream 之后,所有后续值都被发送到返回的ostream。这会让您回到最初问题的起点。为了使其正常工作,您需要将owstream 作为第一个参数并在重载的operator&lt;&lt; 中返回一个owstream

    问题 #2

    另一个问题是owstream 可以隐式转换为std::ostream。根据您在项目中使用owstream 的方式,您提供的重载可能不会在某些情况下产生影响。例如,如果将owstream 类型的对象传递给接受std::ostream 的函数,您最终可能会遇到当前遇到的问题。您可以通过使用private 继承来防止这种情况发生。这将防止将owstream 用作std::ostream 的任何隐式使用。使用private 继承还有一个好处是可以防止您在不知不觉中使用std::ostream 中的函数,这可能会导致您回到原来的问题。对于绝对需要std::ostream 的情况,您可以使用访问器函数显式检索对它的引用。

    问题 #3

    最后一个问题是std::ostream 包含operator&lt;&lt; 的重载,它处理std::ostream 特定的IO 操作符,例如std::endl。如果您不提供重载来专门处理这些问题,std::ostream 中的那个就会被使用,并且您再次回到开始的地方。如果您使用上述private 继承并且不提供重载来处理操纵器,它将无法编译。


    解决方案

    下面的解决方案类似于JRG 提供的解决方案,包括doublefloatstd::ostream IO 操纵器的访问器函数和重载。这是一个完整的工作示例,为了简单起见,它使用来自std::cout 的流缓冲区。我将float 的重载包括在内,因为有缺陷的库实现可能与它们有类似的问题。它还使用私有继承来防止隐式转换为std::ostream

    我在 VC++10 和 GCC 4.7.2 上对其进行了测试。您可能需要根据编译器和库的合规程度进行一些调整。

    #include <ostream>
    #include <iostream>
    #include <sstream>
    
    class owstream : private std::ostream
    {
    public:
        owstream(std::streambuf* sb)
            : std::ostream(sb)
        {}
    
        // Template based operator...ohhhhhh ahhhhh.
        template <typename T>
        friend owstream& operator<<(owstream&, const T&);
    
        // Additional overload to handle ostream specific io manipulators 
        friend owstream& operator<<(owstream&, std::ostream& (*)(std::ostream&));
    
        // Accessor function to get a reference to the ostream
        std::ostream& get_ostream() { return *this; }
    };
    
    
    template <typename T>
    inline owstream&
    operator<<(owstream& out, const T& value)
    {
        static_cast<std::ostream&>(out) << value;
        return out;
    }
    
    //  overload for double
    template <>
    inline owstream&
    operator<<(owstream& out, const double& value)
    {
        std::stringstream ss;
        ss << value;
        return out << ss.str();
    }
    
    //  overload for float
    template <>
    inline owstream&
    operator<<(owstream& out, const float& value)
    {
        std::stringstream ss;
        ss << value;
        return out << ss.str();
    }
    
    //  overload for std::ostream specific io manipulators
    inline owstream&
    operator<<(owstream& out, std::ostream& (*func)(std::ostream&))
    {
        static_cast<std::ostream&>(out) << func;
        return out;
    }
    
    int main()
    {
        owstream ows(std::cout.rdbuf());
    
        ows << std::endl;
        ows << "hello " << 1.0 << " " << 2.0f << std::endl;
    }
    

    【讨论】:

    • 感谢这个建议,但在我的情况下,它会像这样使用:` owstream ows(std::cout.rdbuf()); std::ostream& ss = ows.get_ostream(); ss
    • 执行std::ostream&amp; ss = ows.get_ostream(),然后使用ss &lt;&lt; value不会起作用,因为运算符是ostream 的非虚拟成员(无论如何在符合标准的实现中)。您最好的选择是在项目中将所有ostream 的使用替换为owstream。使用private 继承在这里肯定会有所帮助,因为它会阻止执行隐式转换。这将导致编译失败,直到所有内容都被重构以使用它。
    【解决方案2】:

    这样的事情怎么样:

    template <typename T>
    owstream& operator<<(owstream&, const T&);
    
    template <typename T>
    inline owstream&
    operator<<(owstream& os, const T& val)
    {
       std::ostream& stdos = static_cast<std::ostream&>(os);
       stdos << val;
       return os;
    }
    
    template <>
    inline owstream&
    operator<<(owstream& os, const double& val)
    {
       std::stringstream ss;
       ss << val;
       os << ss.str();
       return os;
    }
    

    【讨论】: