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

时间:2015-03-27 07:03:13

标签: c++ argument-dependent-lookup

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

#include <iostream>

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

Ideone link

如何编译?我确信全局范围内没有endl,因为像

这样的代码
std::cout << endl;
除非使用using否则

会失败,否则您需要std::endl

2 个答案:

答案 0 :(得分:24)

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

例如:

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
}

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

endl命名空间as following中声明了

std::endlstd

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名称空间中搜索不合格的函数std并且它被成功发现并确认为函数,因此调用了合格函数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?

答案 1 :(得分:3)

这是流操纵器的工作方式。 操纵器是传递给操作员的功能&lt;&lt;作为参数。然后在操作员中简单地调用它们。

所以你的函数声明为

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

并将其指针传递给operator&lt;&lt;。并且在声明类似

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

该函数被调用。像

return (*endl )(*this);

因此,当你看到记录

std::cout << std::endl;

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

在记录中

std::endl( std::cout );
可以省略名称endl之前的

名称空间前缀,因为在这种情况下,编译器将使用Argument Dependent Lookup。这样的记录

endl( std::cout );

将成功编译。

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

( endl )( std::cout );

不会编译。