【问题标题】:Templated operator<< isn't being recognized模板化操作符<< 未被识别
【发布时间】:2015-03-03 22:19:42
【问题描述】:

我创建了一个名为 SkipToChar 的类,它应该可以按如下方式使用:

std::ostringstream oss;
oss << "Hello," << SkipToChar(7) << "world!" << std::endl;

将打印“Hello, world!” (注意空格。)基本上它应该使用空格跳到指定索引处的字符。但显然编译器无法识别我为它创建的operator&lt;&lt;。有趣的是,显式调用operator&lt;&lt;,即使没有提供任何模板参数(如operator&lt;&lt;(oss, SkipToChar(7)); 工作正常;如果我实际使用它就行不通

这是我的代码:

#include <iostream>
#include <sstream>

template <typename _Elem>
struct basic_SkipToChar
{
    typename std::basic_string<_Elem>::size_type pos;
    basic_SkipToChar(typename std::basic_string<_Elem>::size_type position)
    {
        pos = position;
    }
};

template <typename _Elem>
inline std::basic_ostringstream<_Elem> &operator<<(std::basic_ostringstream<_Elem> &oss, const basic_SkipToChar<_Elem> &skip)
{
    typename std::basic_string<_Elem>::size_type length = oss.str().length();
    for (typename std::basic_string<_Elem>::size_type i = length; i < skip.pos; i++) {
        oss << (_Elem)' ';
    }
    return oss;
}

typedef basic_SkipToChar<char> SkipToChar;
typedef basic_SkipToChar<wchar_t> WSkipToChar;

int main(int argc, char *argv[])
{
    std::ostringstream oss;
    /*ERROR*/ oss << "Hello," << SkipToChar(8) << "world!" << std::endl;
    std::cout << oss.str();
    return 0;
}

当我尝试编译它时,它给了我以下错误:

error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'basic_SkipToChar<char>' (or there is no acceptable conversion)

我用注释标记了错误所在的行。这里出了什么问题?

【问题讨论】:

  • 最好只重载operator&lt;&lt;(std::ostream&amp;, whatever),因为std::ostream&amp; 是这些运算符通常返回的,因此在连接调用时会得到什么,如out &lt;&lt; a &lt;&lt; b &lt;&lt; c;

标签: c++ templates operator-overloading operators stringstream


【解决方案1】:

oss &lt;&lt; "Hello," 返回std::basic_ostream&lt;char&gt;(它丢失字符串部分)。
所以你的方法不匹配(期望std::basic_ostringstream,但得到std::basic_ostream&lt;char&gt;)。

【讨论】:

    【解决方案2】:

    正如 Jarod42 指出的,内置的 all 的返回类型 operator&lt;&lt;std::ostream&amp;。这是一个基本原则 如何流操作符;除了实际的汇或源, 流应该对数据的去向或去向无动于衷 来自。

    可以提供只为一个人工作的操纵器 流类型,或为不同类型的流做不同的事情 流,通过在操纵器中使用dynamic_cast

    std::ostream&
    operator<<( std::ostream& dest, SkipToChar const& manip )
    {
        std::ostringstream* d = dynamic_cast<std::ostringstream*>( &dest );
        if ( d != nullptr ) {
            //  ...
        }
        return dest;
    }
    

    但是,总的来说,这是一个非常糟糕的主意;客户将 很惊讶输出到字符串会导致不同的结果 文本而不是输出到文件。

    你似乎试图做的是或多或少地效仿 一种标签形式。最好的方法是使用 一个过滤输出流缓冲区,它跟踪你在哪里 在行中,并且可以插入在std::ostream 和 无论流的类型如何,实际接收器:

    class TabbingOutputStreambuf : public std::streambuf
    {
        std::streambuf* myDest;
        std::ostream*   myOwner;
        int             myInLineCount;
    public:
        TabbingOutputStreambuf( std::streambuf* dest )
            : myDest( dest )
            , myOwner( nullptr )
            , myInLineCount( 0 )
        {
        }
        TabbingOutputStreambuf( std::ostream& dest )
            : myDest( dest.rdbuf() )
            , myOwner( &dest )
            , myInLineCount( 0 )
        {
            myOwner.rdbuf( this );
        }
        ~TabbingOutputStreambuf()
        {
            if ( myOwner != nullptr ) {
                myOwner->rdbuf( myDest );
            }
        }
    
        int overflow( int ch ) override
        {
            if ( ch == '\n' ) {
                myInLineCount = 0;
            } else {
                ++ myInLineCount;
            }
            return myDest->sputc( ch );
        }
    
        //      Special function...
        int tabTo( int n )
        {
            int retval = 0;
            while ( retval == 0 && myInLineCount < n ) {
                if ( overflow( ' ' ) == EOF ) {
                    retval = EOF;
                }
            }
            return retval;
        }
    };
    

    你的操纵者将是:

    std::ostream&
    operator<<( std::ostream& dest, SkipToChar const& manip )
    {
        TabbingOutputStreambuf* sb = dynamic_cast<TabbingOutputStreambuf*>( dest.rdbuf() );
        assert( sb != nullptr );
        try {
            if ( sb->tabTo( manip.pos ) == EOF ) {
                dest.setstate( std::badbit );
            }
        } catch ( ... ) {
            dest.setstate( std::badbit );
        }
        return dest;
    }
    

    这仍然不理想,因为它会因未受保护而失败 缓冲区,但您只能在以下上下文中使用操纵器:

    void
    generateOutput( std::ostream& dest )
    {
        TabbingOutputStreambuf tabber( dest );
        dest << "Hello, " << SkipToChar( 7 ) << "world!";
        //  ...
    }
    

    无论传递给什么类型的流,这都会起作用 该功能(包括您不使用的自定义 ostream 类 甚至知道)。

    编辑:

    最后一点:在基本版本正常工作之前,不要费心制作模板。 (对于它的价值,无论如何,您的代码对于 wchar_t 流都无法正常工作。要在其中输出空格,您需要获取嵌入式语言环境,从中获取 ctype 方面,并使用其 widen 成员函数.)

    【讨论】:

    • 谢谢!我采用了一种更简单的方法,它只适用于字符串流;这就是我所需要的。
    • @flarn2006 如果机械手的能见度有限,没问题。这通常是个坏主意,因为客户会感到惊讶;如果它是在没有客户端的情况下完成的,那么这个一般规则不适用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-12
    • 2012-02-28
    • 1970-01-01
    • 2013-04-10
    • 2016-08-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多