put_time进入流奇怪行为

时间:2018-08-14 00:37:12

标签: c++ stream c++14

据我了解,重载运算符由编译器转换为方法调用,例如:

stream << "test";

与:

stream.operator<<("test");

如果确实如此,那为什么会导致编译错误:

#include <iostream>
#include <iomanip>
#include <ctime>

int main() 
{
   std::ostream s {std::cout.rdbuf()};
   auto t = std::time(nullptr);
   auto tm = *std::localtime(&t);
   s.operator<<(std::put_time(&tm, "%H:%M:%S"));//doesn't work
   return 0;
}

doesn't work行更改为:

s << std::put_time(&tm, "%H:%M:%S");

使代码编译。

C ++ 14上的GCC和MSVC 2017似乎都产生错误。这是编译器错误还是这两个语句不相等?

1 个答案:

答案 0 :(得分:2)

这是您的代码,但已减少:

struct X { friend void operator+(X, X); };

int main() {
  X var;
  var + var; // ok
  var.operator+(var); // fail
}

问题是,通过显式调用operator<<,您所依赖的事实是它确实是类的运算符(如T& T::operator<<(const U&);),而不是在类外部定义的运算符(因为如果操作员不是成员,则不能写var.operator<<(/*...*/))。

如果您编写var << something,则除了在类中定义的运算符之外,查找(称为ADL)还将查找在类外部定义的运算符。

可以在类之外定义运算符,因为std::put_time的返回类型指定为([ext.manip]

  

未指定类型的对象,例如,如果out是类型basic_­ostream<charT, traits>的对象,则表达式out << put_­time(tmb, fmt)的行为就像调用了f(out, tmb, fmt)一样,其中函数{{ 1}}定义为:[...]

由于要求的格式为f,因此没有任何东西禁止标准库的实现在类外定义它,以便可以使用ADL找到它。