模板化算子<<没有得到承认

时间:2015-01-06 09:14:38

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

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

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

哪个会打印“Hello,world!” (注意空格。)基本上它应该使用空格跳转到指定索引处的字符。但显然编译器无法识别我为它创建的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)

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

2 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:1)

正如Jarod42指出的那样,内置的所有的返回类型 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流。要在其中输出空格,你需要获得嵌入式语言环境,从中获取ctype方面,并使用其widen成员函数。)