【问题标题】:Using a << operator inside an overloaded << operator在重载的 << 运算符中使用 << 运算符
【发布时间】:2015-03-13 04:57:02
【问题描述】:

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

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");
         ^

显然,编译器将相同的重载 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

因为&lt;&lt; (float) 之后的返回类型是SmartStream,它不知道如何处理struct tms。

我的问题:

有没有办法说服编译器(gcc 4.8.2)对 std::ostringstream 使用'base' in.m_buf << f; 的代码移动到 SmartStream 中的非模板函数,但没有任何帮助。

另外,为什么它只在模板运算符

【问题讨论】:

  • 我只看了你的代码,但你试过in.m_buf.ostrongstream::operator&lt;&lt;(f)吗?请注意,您需要一个 using 指令,因为成员访问权限不能是命名空间限定的。
  • 你的模板朋友太笼统了,他们什么都拿,在那里更具体。
  • @dyp:很有趣。 gist.github.com/sharth/6b09bd9e5aca4585370c 使用 clang++ 编译良好,但使用 g++ 4.9.0 编译失败...
  • @5gon12eder:好吧,in.m_buf::ostringstream::operator &lt;&lt; fin.m_buf.ostringstream::operator &lt;&lt; f 都不起作用。除非您可以指定正确的 using 指令,否则 using std::ostringstream::operator&lt;&lt; 将不起作用(我不确定这是否可行,因为它是一个运算符,而不是一个函数)

标签: c++ templates gcc


【解决方案1】:

我在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 子句,只是我没想到的。

【讨论】:

    【解决方案2】:

    所以,我认为最简单的方法是使用 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.
    }
    

    【讨论】:

    • 这有点 hacky,并且需要(至少对于 gcc 4.8)启用实验性 C++11 功能,但它可以工作。
    • @JvO: enable_if 存在于 c++11 前编译器的 boost 中,您也可以始终创建私有实现。 (它不依赖于任何新的编译器特性来实现)。
    • 如果你想在SmartStream的任何成员函数中使用std::cout &lt;&lt; 1.0f,这也是必要的。
    猜你喜欢
    • 1970-01-01
    • 2012-11-26
    • 2017-12-23
    • 1970-01-01
    • 1970-01-01
    • 2015-04-12
    • 2016-02-19
    • 1970-01-01
    相关资源
    最近更新 更多