为什么 endl(std::cout) 编译

Why does endl(std::cout) compile

令人惊讶的是,下面的代码在各种编译器和版本上编译和运行都没有错误。

#include <iostream>

int main() {
    endl(std::cout);
    return 0;
}

Ideone link

它是如何编译的?我确定在全局范围内没有 endl 因为像

这样的代码
std::cout << endl;

会失败,除非使用 using 否则你需要 std::endl.

此行为称为 argument dependent lookup 或 Koenig 查找。该算法告诉编译器在查找 unqualified 函数调用时,不仅要查看本地范围,还要查看包含参数类型的命名空间。

例如:

namespace foo {
  struct bar{
    int a;
  };

  void baz(struct bar) {
    ...
  }
};

int main() {
  foo::bar b = {42};
  baz(b);  // Also look in foo namespace (foo::baz)
  // because type of argument(b) is in namespace foo
}

关于问题文本中提到的代码片段:

endlstd::endlstd 命名空间 as following:

中声明
template< class CharT, class Traits >
std::basic_ostream<charT,traits>&     endl( std::basic_ostream<CharT, Traits>& os );

std::ostream& endl (std::ostream& os);

coutstd::coutdeclared as

extern std::ostream cout;

所以调用 std::endl(std::cout); 完全没问题。

现在当调用 endl(std::cout); 时,因为参数类型 cout 来自 std namespace,不合格的函数 endl 在 [=16= 中搜索] 命名空间,它被成功找到并确认是一个函数,因此调用了合格函数 std::endl


进一步阅读:

  1. GOTW 30: Name Lookup

  2. Why does 'std::endl' require the namespace qualification when used in the statement 'std::cout << std::endl;", given argument-dependent lookup?

这是流操纵器的工作方式。 操纵符是作为参数传递给 operator << 的函数。然后在运算符中简单地调用它们。

所以你的函数声明为

template <class charT, class traits>
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os);

然后你将它的指针传递给运算符<<。在声明类似

的运算符内部
ostream& ostream::operator << ( ostream& (*op)(ostream&));

函数是called.like

return (*endl )(*this);

因此当你看到记录时

std::cout << std::endl;

然后 std::endl 是作为参数传递给 operator << 的函数指针。

在记录中

std::endl( std::cout );
名称 endl 之前的

命名空间前缀可以省略,因为在这种情况下编译器将使用参数相关查找。因此这个记录

endl( std::cout );

将编译成功。

但是,如果将函数名称括在括号中,则不使用 ADL 并记录以下内容

( endl )( std::cout );

不会编译。