令人惊讶的是,下面的代码在各种编译器和版本上编译并运行时没有错误。
#include <iostream>
int main() {
endl(std::cout);
return 0;
}
如何编译?我确信全局范围内没有endl
,因为像
std::cout << endl;
除非使用using
否则会失败,否则您需要std::endl
。
答案 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::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
为declared as
extern std::ostream cout;
所以调用std::endl(std::cout);
完全没问题。
现在,当一个人只调用endl(std::cout);
时,因为参数cout
的类型来自std namespace
,所以在endl
名称空间中搜索不合格的函数std
并且它被成功发现并确认为函数,因此调用了合格函数std::endl
。
进一步阅读:
答案 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 );
不会编译。