我正在阅读关于参数依赖查找的C++编程语言,第 4 版(由 Bjarne Stroustrup 撰写(。这是引用(26.3.6,过度激进的ADL(:
依赖于参数的查找(通常称为 ADL(对于避免冗长 (14.2.4( 非常有用。例如:
#include <iostream> int main() { std::cout << "Hello, world" << endl; // OK because of ADL }
如果没有依赖于参数的查找,将无法找到
endl
操纵器。实际上,编译器注意到要<<
的第一个参数是std
中定义的ostream
。因此,它在std
中查找endl
并找到它(在<iostream>
中(。
这是编译器(C++11 模式(生成的结果:
prog.cpp: In function ‘int main()’:
prog.cpp:4:36: error: ‘endl’ was not declared in this scope
std::cout << "Hello, world" << endl;
^
这要么是编译器中的错误,要么是书中的错误。标准怎么说?
更新:
我需要澄清一点。我知道正确的答案是使用std::endl
.问题是关于书中的文字。正如Lachlan Easton已经说过的那样,这不仅仅是一个错字。整个段落(可能(是错误的。如果这本书是另一位(鲜为人知的(作者,我可以接受这种错误,但我曾经(现在仍然(怀疑,因为它是由Bjarne写的。
这不是编译器中的错误。ADL 用于查找函数而不是参数。 operator<<
是通过 ADL 通过查看参数 std::cout
和(应该是(std::endl
找到的函数。
对于那些说这是一个错字的人来说,事实并非如此。 要么是Bjarne犯了错误,要么是编译器错了。OP发布的段落之后的段落如下:
如果没有依赖于参数的查找,endl 操纵器就不会 发现。实际上,编译器注意到要<<的第一个参数是 在标准中定义的 ostream 。因此,它在 std 中查找 endl 和 找到它(在
<iostream>
(。
正如其他人已经指出的那样,这是书中的一个错字。然而,书中的意思是我们必须
写
std::operator<<(std::cout, "Hello, world").operator<<(std::endl);
没有 ADL。这就是Bjarne所说的冗长。
我站正了。正如拉克兰·伊斯顿(Lachlan Easton(指出的那样,这不是错字,而是书中的错误。我无法接触到这本书,这就是为什么我无法阅读那段并自己意识到它。我已经向Bjarne报告了这个错误,以便他能够纠正它。
有趣。同样的例子在维基百科上和
请注意,
std::endl
是一个函数,但它需要完全限定, 因为它被用作operator<<
(std::endl
是一个函数的参数 指针,而不是函数调用(。
毫无疑问,这是书中的一个错误。尽管如此,std::operator<<(std::cout, "Hello, world").operator<<(std::endl);
示例显示了 ADL 如何帮助减少详细程度。
感谢gx_指出我的错误。
提示在名称"依赖于参数的查找"中。
它是对非限定函数名称的查找,其工作取决于参数。
这与查找参数无关。
比亚恩说错了话。
这本书,但这似乎是书中的一个错误,它缺少命名空间限定符的事实与 ADL 无关。应该是std::endl
.
是的,这是一个错误 - 该示例格式不正确,不应编译。 ADL 适用于引入函数调用表达式的非限定函数名称。 endl
是尝试查找std::endl
的 id 表达式。 endl
没有引入函数调用表达式,因此不对它使用依赖于参数的查找,只使用非限定查找,因此它不会按预期找到std::endl
。
一个更简单和正确的示例是:
#include <vector>
int main()
{
std::vector<int> x, y;
swap(x,y); // calls std::swap due to ADL
}
总之,在查找具有非限定id(例如f
(的函数调用(例如f(x,y,z)
(之前,首先分析函数的参数(例如x,y,z
(以确定其类型。 关联命名空间的列表基于类型形成(例如,类型定义的封闭命名空间是关联的命名空间(。 然后,还会在这些命名空间中搜索函数。
Bjarne的例子的目的是炫耀std::operator<<
函数的ADL,而不是std::endl
。 这需要进一步了解重载运算符实际上是函数调用表达式,因此x << y
表示operator<<(x,y)
,而operator<<
是一个非限定名,因此 ADL 适用于它。 LHS 的类型std::ostream
因此std
是关联的命名空间,因此可以找到std::operator<<(ostream&, ...)
。
更正后的附言应为:
如果没有依赖于参数的查找,则不会找到
std
命名空间中重载的<<
运算符。实际上,编译器注意到要<<的第一个参数是在 std 中定义的 ostream。因此,它在 std 中查找运算符<<
并找到它(在<iostream>
中(。