【问题标题】:如何为所有 C++ IOStream 操作符通用定义插入操作符?
【发布时间】:2011-08-28 10:55:55
【问题描述】:

全部,

为什么下面的代码无法为 'std::endl' 编译,但对于所有其他插入的类型都可以?

#include <sstream> // ostringstream

/// @brief A class that does streamed, formatted output via 'operator<<'.
class My_Stream
{
public:
    /// @brief A member method that manipulates the underlying stream.
    void foo()
    {
        m_oss << "foo_was_here; ";
    }

private:
    /// @brief The underlying stream.
    std::ostringstream m_oss;

    /// @brief 'operator<<' is a friend.
    template< typename T >
    friend My_Stream& operator<<( My_Stream& a_r_my_stream,
                                  const T& a_r_value );
};

/// @brief A manipulator that calls a class method.
My_Stream& manipulator_foo( My_Stream& a_r_my_stream )
{
    a_r_my_stream.foo();
    return a_r_my_stream;
}

/// @brief The generic insertion operator.
template< typename T >
My_Stream& operator<<( My_Stream& a_r_my_stream,
                       const T& a_r_value )
{
    a_r_my_stream.m_oss << a_r_value;
    return a_r_my_stream;
}

/// @brief Define an iostream-like manipulator for my-stream.
typedef My_Stream& ( * my_stream_manipulator ) ( My_Stream& );

/// @brief The specialized 'my_stream_manipulator' insertion operator.
template<>
My_Stream& operator<<( My_Stream& a_r_my_stream,
                       const my_stream_manipulator& a_r_manipulator )
{
    return a_r_manipulator( a_r_my_stream );
}

int main( int argc, char* argv[] )
{
    My_Stream my_stream;

    my_stream << 'c'; // char
    my_stream << "string"; // c-string
    my_stream << 1u; // unsigned int
    my_stream << -1; // signed int
    my_stream << 5.3f; // float
    my_stream << -23.345; // double
    my_stream << std::boolalpha; // std::ios_base manipulator
    my_stream << std::endl; // std::ostream manipulator
    my_stream << manipulator_foo; // my_stream manipulator

    return 0;
}

我收到以下 G++ 4.5 错误:

willo:~/test_cpp$ g++ -Wall test_overloaded_insertion_manipulators.cpp test_overloaded_insertion_manipulators.cpp:在函数“int main(int, char**)”中: test_overloaded_insertion_manipulators.cpp:60: error: no match for ‘operator

我希望代码为 std::endl 实例化一个“运算符

对于上下文,我正在尝试创建一个与当前 IOStream 操纵器以及一两个更多自定义操纵器一起使用的轻量级 API IOStream 类。

【问题讨论】:

  • 你确定公开继承ostringstream 不是更实用吗?您仍然可以添加自定义功能,但不需要费心去获取 stringstream 本身已经可以做的事情。
  • @leftaroundabout:大多数 STL 对象并非真正设计为派生自 std::ostringstream,但似乎至少有一个虚拟析构函数,因此它可能会起作用。
  • @leftaroundabout 是的,我确定。这是一个调试我当前问题的玩具示例。我的预期代码必须拦截每个插入操作,并有条件地做事;所以我必须针对我的新课程重新实现'operator

标签: c++ operator-overloading iostream manipulators


【解决方案1】:

因为endl是一个函数模板:

template <class charT, class traits>
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os);

所以标识符本身不是一个值。它仅在实例化时才成为一个值(函数指针)。但是您的operator&lt;&lt; 本身就是一个模板,编译器没有可用的类型信息来决定使用哪些类型来实例化endl

相反,例如boolalpha 是:

ios_base& boolalpha(ios_base& str);

这就是为什么它有效。

endl 适用于basic_ostream,因为它将operator&lt;&lt; 重载定义为采用函数指针的成员函数;特别是:

basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& (*pf)(basic_ostream<charT,traits>&));

因此,在像stream &lt;&lt; endl 这样的调用中,它会从this 的类型(即运算符的左侧)中知道charTtraits,这将为其提供期望的函数指针的确切类型右侧 - 然后它将用于实例化 endl 的相应版本。你可以为你的班级做同样的事情。

【讨论】:

    【解决方案2】:

    您需要将流操纵器定义为单独的朋友。

    添加此朋友:

    // Note: Untested (don't have a compiler here).
    template <class charT, class Traits>
    friend My_Stream& operator<<( My_Stream&, std::basic_ostream<charT, Traits>& (*)(std::basic_ostream<charT, Traits>&));
    

    然后你需要定义函数:

    template <class charT, class Traits>
    My_Stream& operator<<( My_Stream& stream, std::basic_ostream<charT, Traits>& (*manip)(std::basic_ostream<charT, Traits>&))
    {
        (*manip)(stream.m_oss);
        return stream;
    }
    

    【讨论】:

    • 真的应该被模板化吗?由于stream.m_oss 是一种特定类型,因此只有std::ostream&amp; (*)(std::ostream&amp;) 可以在此处实际使用。
    • 该模板不是用于 My_Stream 而是用于 std::endl 这是我们担心的模板(这就是我们首先遇到问题的原因(所以是的,您需要模板)) .
    • +1 这比我对他的模板专业化的扩展要好,因为它适用于所有可以定义的std::endl 模板类型。
    • 不,额外的operator&lt;&lt; 需要是模板,原因@Pavel 解释。 codepad.org/VKIghrZz
    • @aschepler 你在某种意义上是正确的;我想要处理的任何 IOStream 操纵器都将成为“ostringstream”层次结构的一部分,例如“ostream”、“ios”和“ios_base”。我正在考虑为每个参数类型显式覆盖“operator
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-06
    • 1970-01-01
    相关资源
    最近更新 更多