【问题标题】:Is it possible to overload operator<< like this? [duplicate]是否可以像这样重载 operator<< ? [复制]
【发布时间】:2013-12-16 03:04:18
【问题描述】:

假设我有一个名为“Logger”的类。这个名字是不言自明的,它记录了一些东西。我有一个静态方法,它记录东西(Logger::log(string msg))。 我想重载运算符

    Logger << "AW YEAH, I LOVE C++";

我试图这样做,但做不到。我管理的是这样的:

    Logger l; 
    l << ":(";

...

我想做的事有可能吗?如果是,如何?

提前谢谢你:)

【问题讨论】:

  • 你能用你的实际格式化代码更新你的问题吗?
  • @H2CO3:虽然好像是这个问题的重复,但是引用问题的答案好像全错了!

标签: c++ overloading operator-keyword


【解决方案1】:

有可能,但你需要弄清楚你真正想要什么接口。一种选择是让您的Logger 成为std::ostream(继承),然后事情就会开箱即用。另一种方法是维护一个与std::ostream 无关(无继承)的记录器,但是您需要为要记录的任何和所有类型T 提供Logger&amp; operator&lt;&lt;(Logger&amp;,T const &amp;)。对于std::string 的特殊情况,您可以这样做:

Logger& operator<<(Logger& logger, std::string const& msg) {
   logger.log(msg);               // need not be, and problably should be static
   return logger;
}

使这个泛型与 C++ 中的大多数泛型代码一样涉及模板的使用:

template <typename T>
Logger& operator<<(Logger& logger, T const & obj) {
   // do specific work based on the type, or a completely generic approach:
   std::ostringstream st;
   st << obj;
   logger.log(st.str());
   return logger;
}

此时,您可能需要考虑添加对操纵器的支持,并且...好吧,如果您从这条路径开始,那么不要在每个函数中创建 std::ostringstream 可能更有意义,而是创建一个作为一个成员,然后将所有数据转储到该流中并使用一些操纵器来提取字符串并写入它(例如在std::flushstd::ends...上)

template <typename T>
Logger& operator<<(Logger& logger, T const& obj) {
   logger.stream << obj;
   return logger;
}
Logger& operator<<(Logger& logger, std::ostream& (*manip)(std::ostream&)) {
   logger.stream << manip;
   if (manip == static_cast<std::ostream& (*)(std::ostream&)>(std::flush)) {
      logger.write(); // flush to disk
   }
   return logger;
}
// add support for other manipulators...

然后你可能又开始想知道,将一些现成的日志库拉出来并直接使用它会不会更容易。

【讨论】:

    【解决方案2】:

    假设您想利用std::ostream 提供的输出运算符,您应该尝试重载输出运算符!相反,您将创建一个合适的流缓冲区(即,派生自 std::streambuf 的类)并在该类的 overflow()sync() 方法中实现您的自定义输出逻辑。然后,您的记录器将从 std::ostream 派生并初始化其基类以使用您的自定义流缓冲区。

    【讨论】:

    • 假设我在游戏中需要这个 Logger。使用 'std::ostream' 会产生不必要的开销,对吗?
    • 我真的很想知道 anyone 是否曾从 std::streambuf...
    • @VilmantasBašinovas:我假设您不仅对记录字符串文字感兴趣(如果是这种情况,只需为您的班级重载 operator&lt;&lt;() 以将 char const* 作为参数)想要获得其他类型的格式,这些类型定义了输出运算符(例如,intdouble):写入std::ostream 是最好的选择。我也怀疑合理数量的日志记录是否会导致性能下降,即使在游戏中也是如此! (更不用说我能想象出比游戏更受时间限制的东西……)
    • @KerrekSB:嗯,我经常这样做。鉴于这很容易,我看不出人们为什么不这样做的任何理由。此外,我确信我不是唯一一个:这是正确的做法。
    【解决方案3】:

    如果Logger 是一个类的名称,那么你当然不能这样做。使用类似的东西

    Logger &log() {
        static Logger l;
        return l;
    }
    
    log() << "And the answer is" << 42;
    

    【讨论】:

    • 这就是 Qt 有一个qDebug 类的方式。
    • 从技术上讲,它是一个宏,而不是一个类。但是是的,原理是一样的(返回一个可以使用operator&lt;&lt;的对象)。
    【解决方案4】:

    重载运算符对进行操作,而不是类型

    也许你应该重命名你的类,然后创建一个合适的全局对象。例如:

    logger.h:

    class mylogger { /* ... */ };
    
    extern mylogger logger;
    

    logger.cpp:

    #include "logger.h"
    
    mylogger logger;
    

    用法:

    #include "logger.h"
    
    logger << "Done";
    

    注意全局初始化问题;尽管;查看您对 std::cout 的实现,以了解解决此问题的方法(例如使用 Schwartz 计数器)。

    【讨论】:

    • 我不认为他需要全局...在他想要的最终用法示例中:Logger l; l &lt;&lt; "(...",创建一个记录器l,然后调用operator&lt;&lt; .
    • @DavidRodríguez-dribeas:我对此表示怀疑。他说“我管理的只是 X,但我想要 Y”。
    • 我的错,我不太明白他想要什么。我期待一些更有意义的东西;)(而不是单例)
    • @DavidRodríguez-dribeas:std::cout 不再是单身人士了...
    • 好吧,我无法实现我的目标,我发现全局对象的想法很有吸引力。看来我不是 C++ 专业人士,不太了解 sdt::coutoperator&lt;&lt; 有什么关系?还有什么问题?
    猜你喜欢
    • 2019-02-28
    • 1970-01-01
    • 2013-10-23
    • 2013-01-15
    • 1970-01-01
    • 1970-01-01
    • 2018-04-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多