【问题标题】:Custom class ostringstream output error自定义类 ostringstream 输出错误
【发布时间】:2012-04-30 15:59:49
【问题描述】:

我不知道为什么会出错,但我只是想在 endl 中添加一些“类似”的东西,以便我可以将 ostringstream 中的内容扔到我们的调试器中。我有以下内容:

class debug_stream_info
{
public:
    debug_stream_info(int errorLine, char *errorFile, int level)
        :m_errorLine(errorLine), m_errorFile(errorFile), m_logLevel(level)
    {
    }

    friend std::basic_ostringstream<char>& operator<<(std::basic_ostringstream<char>& os, debug_stream_info& debug_info);

private:
    int m_errorLine;
    std::string m_errorFile;
    int m_logLevel;

};

std::basic_ostringstream<char>& operator<<(std::basic_ostringstream<char>& os, debug_stream_info& debug_info)
{
    // Write the stream's contents to cpu_debug
    // Deleted custom logging function.  No errors here though

    // Clear the stream for re-use.
    os.str("");
    os.seekp(0);

    return os;
}

int main(int argc, char** argv)
{
    std::ostringstream myout;
    myout << "hey there" << " and some more " << "Numbers!!: " << 435 << 54.2 << " that's good for numbers" << debug_stream_info(__LINE__, __FILE__, LOG_LEVEL);

    return 0;
}

我得到的错误是:error C2679: binary '&lt;&lt;' : no operator found which takes a right-hand operand of type 'debug_stream_info' (or there is no acceptable conversion) 用于 main 中的行。这是在 VS2008 上。

我包括 sstream、iostream 等,并正确设置了命名空间。我没有收到其他错误。我什至尝试用ostringstream 替换所有出现的basic_ostream 并且没有区别(我稍后会有w_char 版本,但我希望这个简单的案例首先工作)。我在上面一行做了对象,然后在行上传递了一个完全构造好的对象,报错是完全一样的。我已将第二个参数的签名更改为constconst,也没有任何更改。

关于我在这里做错了什么有什么想法吗?

编辑:因为每个响应似乎都想把它放在那里,我不能使用 std::ostream 因为我希望它只适用于std::ostringstream(和std::basic_ostringstream)而不适用于任何类型的输出流。此外,该函数无论如何都不会用 ostream 编译,因为我使用的是 os.str() 方法,它不在 ostream 中,只有子类。

【问题讨论】:

  • 我从未见过有人专门为std::basic_ostringstream&lt;char&gt;&amp; 超载。通常人们只是为std::ostream&amp; 超载。
  • 您正在为 std::basic_ostringstream 定义一个模板函数,而您的变量是 std::ostringstream。也许这导致您的模板功能无法使用?
  • 正如我在原帖中所说:I even tried replacing all occurrances of basic_ostream with just ostringstream and there was no difference.. 我不能只使用ostream,因为我只希望这个重载适用于ostringstream,而不是所有ostream 类型

标签: c++ stringstream


【解决方案1】:

您的代码的真正问题是您重载了std::ostringstream 而不是std::ostream。所以如果你写这个,你的代码就可以工作:

debug_stream_info info(/** blah blah**/);

std::ostringstream oss;
oss << info ; //OK

但是这不起作用:

oss << 1 << info; //ERROR

这是编译错误,因为表达式 oss&lt;&lt;1 返回一个类型为 std::ostream&amp; 的对象,该对象没有将 debug_stream_info 作为第二个参数的重载。这意味着如果你使用 cast as:

static_cast<std::ostringstream&>(oss << 1) << info; //OK

那么这应该再次起作用。

所以解决方案是重载std::ostream,而不是std::basic_ostringstream

另外,第二个参数应该是 const &amp; 。这也是你的代码的问题。

所以写这个:

std::ostream& operator<<(std::ostream&, debug_stream_info const &);
                                                        //^^^^^^^ note this

第二个参数应该是const &amp;,这样您就可以将临时对象写入流。

【讨论】:

  • 如上所述,我不能使用 ostream,因为我不希望它适用于所有 ostream,只适用于 ostringstream。我尝试了你为 const 放的东西(在朋友声明和实现中),这是同样的错误。
  • @Kevin:查看我的更新答案。它解释了您的代码的真正问题。
  • @Kevin 这个解决方案对我有用;只需将const 添加到您的debug_stream_info 参数并切换到std::ostream。或者正如@CppLearner 所说,没有临时对象。
  • 所以没有办法在这个东西上强制编译时安全?基本上,我确实需要 ostringstream 的方法,这意味着在运行时进行强制转换。尽管您确定了错误的原因,但我会赞扬您,而不仅仅是“编写其他代码!”就像这里的另一个答案一样。
  • @Kevin:很明显,调用str("")的目的是让流缓冲区为空。如果是这样,并且如果您要重载std::ostream,那么您可以尝试这样做:os.rdbuf()-&gt;pubsetbuf("", 0);。除此之外,也许您还需要做更多的事情。也许,您还需要设置搜索位置。
【解决方案2】:

debug_stream_info(__LINE__, __FILE__, LOG_LEVEL); 正在创建未返回任何内容的未命名对象,因此会出错

#include <iostream>
#include <cstdio>
#include <sstream>
using namespace std;

class debug_stream_info
{
public:
    debug_stream_info(int errorLine, char *errorFile, int level)
        :m_errorLine(errorLine), m_errorFile(errorFile), m_logLevel(level)
    {
    }

    friend std::basic_ostringstream<char>& operator<<(std::basic_ostringstream<char>& os, debug_stream_info& debug_info);
    std::ostringstream& fun(std::ostringstream& os)
    {
        os<<"Ashish"<<endl;
        return os;
    }
private:
    int m_errorLine;
    std::string m_errorFile;
    int m_logLevel;

};

std::basic_ostringstream<char>& operator<<(std::basic_ostringstream<char>& os, debug_stream_info& debug_info)
{
    // Write the stream's contents to cpu_debug
    // Deleted custom logging function.  No errors here though

    // Clear the stream for re-use.
//    os.str("");
//    os.seekp(0);

    return os;
}

int main(int argc, char** argv)
{

    std::ostringstream myout, test;
        myout << "hey there" << " and some more " << "Numbers!!: " << 435 << 54.2 << " that's good for numbers"
         << debug_stream_info(1, "/home/ashish/test", 1).fun(test);
    return 0;
}

【讨论】:

  • 其实效果很好。我试过这个:cout &lt;&lt; "Try Unnamed: " &lt;&lt; std::string("Unnamed!") &lt;&lt; endl;,它工作正常。您可以毫无问题地制作这样的“临时”对象。
  • @Kevin 您可以将临时的std::string 传递给operator&lt;&lt;(),因为它已经有const,就像@Nawaz 所说的那样。
  • 不是问题,或者至少不是唯一的问题。 (但你是对的,他的operator&lt;&lt; 应该通过引用 const 来获取新类型,而不是引用非 const。)
【解决方案3】:

Nawaz 已经非常清楚地解释了您收到错误的原因。这 在这种情况下,通常的解决方案是定义自己的流类型,不相关 到std::istream。类似的东西:

class DebugStream
{
    std::ostringstring* collector;

public:
    template <typename T>
    DebugStream& operator<<( T const& value )
    {
        if ( collector != NULL ) {
            *collector << value;
        }
        return *this;
    }
};

对此有无限的变化;在您的情况下,您可以添加一个 您的类型的非模板成员函数;更有可能的是,您会添加一个 采用相同参数的构造函数:

DebugStream( int lineNumber, std::string const& filename, int logLevel )
    : collector( isActive( logLevel ) ? new std::ostringstream : NULL )
{
    //  Initial insertion of lineNumber, filename, timestamp...
}

您还可以添加一个析构函数,以原子方式刷新收集的 数据到文件(或发送电子邮件,或将其写入系统日志,或 任何)。 (对此要非常小心。你不希望例外 从析构函数中逃脱,即使日志记录失败。)

最后,您可能想要使用自定义的 streambuf,而不是 stringstream。说一个使分配的缓冲区保持不变的一个 实例到下一个。如果你这样做,而不是newing 每次流,您可能会从表中选取一个实例,索引为 日志级别(并从配置文件初始化)。

【讨论】:

  • 一个好主意,我给你的声誉。我忘记了您可以模板化operator&lt;&lt; 来利用其他类已经为自己覆盖该运算符的事实。这是使用 ostringstream 的主要原因:获取所有现有的覆盖。但总的来说,我喜欢你的想法,这可能是我正在做的事情的“通用解决方案”。
猜你喜欢
  • 1970-01-01
  • 2012-02-14
  • 2015-01-12
  • 2020-12-18
  • 2020-07-06
  • 2023-03-10
  • 2019-04-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多