我创建了一个名为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)
我用注释标记了错误所在的行。这里出了什么问题?
答案 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
成员函数。)