【问题标题】:C++11 Adding a stream output operator for std::chrono::time_pointC++11 为 std::chrono::time_point 添加流输出操作符
【发布时间】:2013-05-17 13:00:48
【问题描述】:

我希望能够做到以下几点:

std::cerr << std::chrono::system_clock::now() << std::endl;

并获得以下信息:

Wed May  1 11:11:12 2013

所以我写了以下内容:

template<typename Clock, typename Duration>
std::ostream &operator<<(std::ostream &stream,
  const std::chrono::time_point<Clock, Duration> &time_point) {
  const time_t time = Clock::to_time_t(time_point);
#if __GNUC__ > 4 || \
    ((__GNUC__ == 4) && __GNUC_MINOR__ > 8 && __GNUC_REVISION__ > 1)
  // Maybe the put_time will be implemented later?
  struct tm tm;
  localtime_r(&time, &tm);
  return stream << std::put_time(tm, "%c");
#else
  char buffer[26];
  ctime_r(&time, buffer);
  buffer[24] = '\0';  // Removes the newline that is added
  return stream << buffer;
#endif
}

这行得通,但是从不同的命名空间调用它时,我总是遇到问题。这应该只在全局命名空间中是否正确?

【问题讨论】:

  • std 中没有 system_clock。它在std::chrono
  • 抱歉,更新了正确的修复程序。
  • 请注意,您正在做一些有点不礼貌的事情,因为您正在将一个覆盖的运算符添加到您不“拥有”的两种类型。更重要的是,该标准的下一次迭代可能会编写自己的&lt;&lt; 重载。

标签: c++ c++11 operator-overloading ostream


【解决方案1】:

当您想确保调用正确的函数时,您应该在调用它的代码范围内放置一个using 声明。

例如:

namespace pretty_time {
  /* your operator<< lives here */
}


void do_stuff() {
  using namespace pretty_time;   // One way to go is this line
  using pretty_time::operator<<; // alternative that is more specific (just use one of these two lines, but not both)
  std::cout << std::chrono::system_clock::now();
}

【讨论】:

  • 如果我把操作符放在全局命名空间中,它总能找到吗?不过,这似乎是一种非常安全和明智的方法。谢谢。
  • 我写的 C++ 代码越多,我发现全局命名空间就越没用,老实说。我更喜欢第二种形式using pretty_time::operator&lt;&lt;,因为它不会给读者留下歧义。如果我想让同一个命名空间中的多个名称可用,并且我确信不会引入副作用,我不会反对使用“导入”整个命名空间的第一种形式,但我总是喜欢留下一个注释指出我打算从该命名空间中使用哪些东西。喜欢using namespace pretty_time; // for operator&lt;&lt;
  • 我同意。我并没有真正考虑过使用 using 声明来限定流运算符。其他人可能希望以另一种格式输出时间,这会与我的全局流操作符发生冲突。
  • 另外,如果您有一个类/结构类型存在于命名空间中,则在解析不合格的函数名称时,该命名空间具有更高的优先级。在我的一个宠物项目中可以看到一个示例,它具有表示 IP 地址的类型并提供人类可读的operator&lt;&lt;。 IP 命名空间headerimplementationclient code。请注意缺少using
  • 我没有使用 using 声明,因为该函数与表示 IP 地址的自定义类型位于相同的命名空间中。
【解决方案2】:

一种方法可以让你自己的namespace 保持混乱,并避免在你不拥有的两种类型上重载运算符这种有点不礼貌的事情,那就是让你的输出语法稍微冗长一些:

std::cerr << pretty_print::format(std::system_clock::now()) << std::endl;

如下:

namespace pretty_print {
  template<typename T>
  struct print_wrapper { // boost::noopy optional -- if so, use it with && as an argument
    T const& data;
    print_wrapper( T const& t ): data(t) {}
  };
  template<typename T>
  print_wrapper<T> format( T const& t ) {
    return {t};
  }
  template<typename Clock, typename Duration>
  std::ostream &operator<<(std::ostream &stream,
    print_wrapper<std::chrono::time_point<Clock, Duration>>&& time_point)
  {
    // ...
  }
}

并访问time_point.data 以获取&lt;&lt; 重载中的原始数据。

当您使用 print_wrapper&lt;&gt; 包装类型时,将通过 ADL(参数相关查找)找到 &lt;&lt; 运算符,即使没有将其拉入您使用它的 namespace !要使用它,您可以使用pretty_print::format(blah)using pretty_print::formatformat 拉入当前范围。

实际上,您已将类型 T 标记为用于您自己的自定义重载集。我喜欢这种“瘦类型包装器”技术,因为它让我想起了std::move

这也让你说“我讨厌doubles 的格式”,并引入一个&lt;&lt;,它可以更好地格式化它们,它需要一个print_wrapper&lt;double&gt;

作为附带的好处,您可以专门化/重载 print_wrapperformat 以获取格式化参数 - 因此您可以使用 pretty_print::format( std::system_clock::now(), pretty_print::eDate::YMD )pretty_print::eFmt::compact

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-13
    • 1970-01-01
    • 1970-01-01
    • 2014-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-27
    相关资源
    最近更新 更多