模板化运算符 << 未被识别

Templated operator<< isn't being recognized

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

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

这将打印 "Hello, world!"(注意 space。)基本上它应该使用 space 跳到指定索引处的字符。但显然编译器无法识别我为其创建的 operator<< 。有趣的是,显式调用 operator<<,即使不提供任何模板参数(如 operator<<(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)

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

oss << "Hello," returns std::basic_ostream<char>丢失字符串部分)。
所以你的方法不匹配(期望 std::basic_ostringstream 但得到 std::basic_ostream<char>)。

正如 Jarod42 指出的那样,return 类型的 all 的内置 operator<<std::ostream&。这是一个基本原则 如何流操作员;除了实际的汇或源, 流应该对数据的去向或 来自.

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

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

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

你似乎试图做的是或多或少地效仿 一种标签形式。最好的方法是使用 过滤输出 streambuf,它跟踪你在哪里 在行中,并且可以插入 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 流中正常工作。要在其中输出 space,您需要获取嵌入式语言环境,从 ctype 方面获取它,并使用它的 widen 成员函数。)