使用<<运算符内的重载<<操作者

时间:2015-01-14 21:15:46

标签: c++ templates gcc

我正在编写一个重载<<<<<和>>运算符类似于std :: istream和std :: ostream。前几个功能按预期工作。但是,我想在我自己的类中使用其他std :: *流类来进行格式化,这会导致一个有趣的,我会说,'递归'编译器问题:

class SmartStream
{
public:
    template <typename F> friend F &operator << (F &in, float f)
    {
        in.type ("f");
        in.m_buf.str ("");
        in.m_buf << f;          // This causes a 'recursive' compile call
        in.add (in.m_buf.str ());
        return in;
    }

protected:
    std::ostringstream m_buf;

    void type(const std::string &_type) {};
    void add(const std::string &) {};
};

实际错误很长但是从这样开始:

rec_stream.cpp: In instantiation of 'F& operator<<(F&, float) [with F = std::basic_ostringstream<char>]':
rec_stream.cpp:14:18:   required from 'F& operator<<(F&, float) [with F = ExtraStream]'
rec_stream.cpp:60:11:   required from here
rec_stream.cpp:12:9: error: 'class std::basic_ostringstream<char>' has no member named 'type'
         in.type ("f");
         ^

显然,编译器应用了相同的重载&lt;&lt;运算符到std :: ostringstream类型的m_buf变量,但当然没有type()和add()函数。在阅读this question的答案之后,这有点意义,但没有提供解决方案。

在您说之前没有,请在SmartStream中使用它:

class SmartStream 
{
   ....
    SmartStream &operator << (float f)
    {
        type("f");
        m_buf.str ("");
        m_buf << f;     // Ambiguous overload
        add( m_buf.str ());
        return *this;
    }
};

有两个问题。首先,如代码中所述,触发模糊的重载。其次,请考虑以下事项:

class ExtraStream: public SmartStream
{
public:
    template <typename F> friend F &operator << (F &in, struct tm t)
    {
        in.type ("tm");
        in.m_buf.str ("");
        in.m_buf << t.tm_hour; // Ambiguous overload
        in.add (in.m_buf.str ());
        return in;
    }
};

实际上,我正在使用该类中规定的机制扩展SmartStream来处理自定义类型。使用非朋友操作员将不允许我这样做:

ExtraStream es;

struct tm t1, t2;
float f1, f2;

es << f1 << t1 << f2 << t2; // works

因为返回类型为<< (float)后,SmartStream不知道如何处理struct tm

我的问题:

有没有办法说服编译器(gcc 4.8.2)使用'base'&lt;&lt; std :: ostringstream的运算符而不是重载的运算符?我尝试了各种演员,::解析运算符,将in.m_buf << f;的代码移动到SmartStream中的非模板化函数,但没有任何帮助。

另外,为什么它只在模板运算符中使用它&lt;&lt;功能?任何使用&lt;&lt;在该函数之外的std :: ostringstream上按预期工作。

2 个答案:

答案 0 :(得分:2)

我在gcc buglist发布了这个,因为我觉得这是一个错误,或者至少是C ++参考中的歧义。 Jonathan Wakely提出了一个非常简单的解决方案:

template <typename F> friend F &operator << (F &in, float f)
{
    in.type ("f");
    in.m_buf.str ("");
    using std::operator <<;  // indicate proper operator in the next line
    in.m_buf << f;          
    in.add (in.m_buf.str ());
    return in;
}

所以它确实需要一个using条款,只是我不会期待的条款。

答案 1 :(得分:0)

因此,我认为最简单的方法是使用类似std::enable_if的内容限制友元函数,以便它们仅在具有正确父类的流上有效。

#include <sstream>
#include <type_traits>

class SmartStream {
    public:
        template <typename F>
        friend typename std::enable_if<std::is_base_of<SmartStream, F>::value, F&>::type
        operator << (F &in, float f) {
            in.type("f");
            in.m_buf.str("");
            in.m_buf << f;
            in.add (in.m_buf.str ());
            return in;
        }

    protected:
        std::ostringstream m_buf;

        void type(const std::string &_type) {};
        void add(const std::string &) {};
};

class ExtraStream: public SmartStream {
    public:
        template <typename F>
        friend typename std::enable_if<std::is_base_of<ExtraStream, F>::value, F&>::type
        operator << (F &in, struct tm t) {
            in.type ("tm");
            in.m_buf.str ("");
            in.m_buf << t.tm_hour;
            in.add (in.m_buf.str ());
            return in;
        }
};

int main() {
    SmartStream ss;
    ExtraStream es;
    struct tm t;

    ss << 3.14f;
    es << 3.14f << t << 3.14;
    // ss << t; // Fails as expected.
}