令人惊讶的是,以下代码在各种编译器和版本上都可以编译和运行而没有错误。
#include <iostream>
int main() {
endl(std::cout);
return 0;
}
它是如何编译的?我确定在全局范围内没有 endl
,因为像这样的代码:
std::cout << endl;
如果不使用using
或者你需要std::endl
,否则会失败。
令人惊讶的是,以下代码在各种编译器和版本上都可以编译和运行而没有错误。
#include <iostream>
int main() {
endl(std::cout);
return 0;
}
它是如何编译的?我确定在全局范围内没有 endl
,因为像这样的代码:
std::cout << endl;
如果不使用using
或者你需要std::endl
,否则会失败。
这种行为被称为参数依赖查找或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
或 std::endl
在 std
命名空间中声明,声明如下(链接):
template< class CharT, class Traits >
std::basic_ostream<charT,traits>& endl( std::basic_ostream<CharT, Traits>& os );
或者std::ostream& endl (std::ostream& os);
而 cout
或者 std::cout
被声明为 ...
extern std::ostream cout;
调用std::cout
的std::endl
是完全可以的。
当只调用endl(std::cout)
时,由于参数cout
的类型来自于std命名空间
,未经限定的假定函数endl
会在std
命名空间中搜索,并成功地找到了它,确认它是一个函数,因此会调用合格的函数std::endl
。
进一步阅读:
template <class charT, class traits>
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os);
ostream& ostream::operator << ( ostream& (*op)(ostream&));
该函数被称为“like”。
return (*endl )(*this);
std::cout << std::endl;
那么,std::endl
是函数指针,作为参数传递给 operator <<
。
在记录中
std::endl( std::cout );
名称前的命名空间前缀endl
可以省略,因为在这种情况下编译器将使用参数相关的查找。因此,这条记录可以这样写:
endl( std::cout );
成功编译。
但是,如果将函数名括在括号中,则不会使用ADL,并显示以下记录
( endl )( std::cout );
无法编译。
std::
的原因。 - M.M