81

我正在阅读The C++ Programming Language, 4th Edition (by Bjarne Stroustrup ) 关于。这是引用(26.3.6,过度激进的 ADL):

依赖于参数的查找(通常称为 ADL)对于避免冗长(14.2.4)非常有用。例如:

#include <iostream>

int main()
{
    std::cout << "Hello, world" << endl; // OK because of ADL
}

如果没有依赖于参数的查找,endl将找不到操纵器。实际上,编译器注意到 的第一个参数<<是在ostream中定义的std。因此,它查找endlinstd并找到它 (in <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 写的。

4

6 回答 6

83

这不是编译器中的错误。ADL 用于查找函数而不是参数operator<<是通过 ADL 找到的函数,这里通过查看参数std::cout和(应该是什么)std::endl

于 2013-08-06T17:11:07.980 回答
49

对于那些说这是一个错字的人来说,它不是。Bjarne 犯了一个错误,或者编译器出错了。OP发布的那一段之后的段落为

如果没有依赖于参数的查找,将找不到 endl 操纵器。事实上,编译器注意到 << 的第一个参数是 std 中定义的 ostream。因此,它在 std 中查找 endl 并找到它(in <iostream>)。

于 2013-08-07T03:55:50.523 回答
20

正如其他人已经指出的那样,这是书中的一个错字。然而,这本书的意思是 我们必须写

std::operator<<(std::cout, "Hello, world").operator<<(std::endl);

没有 ADL。这就是 Bjarne 所说的冗长的意思。


我站得更正了。正如拉克兰伊斯顿指出的那样,这不是错字,而是书中的错误。我无法访问这本书,这就是为什么我无法阅读该段落并自己意识到这一点。我已将此错误报告给 Bjarne,以便他纠正。


有趣的。同样的例子是在维基百科

请注意,这std::endl是一个函数,但它需要完全限定,因为它用作operator<<(std::endl是函数指针,而不是函数调用) 的参数。

毫无疑问,这是书中的错误。然而,该示例 std::operator<<(std::cout, "Hello, world").operator<<(std::endl);显示了 ADL 如何帮助减少冗长。


感谢gx_指出我的错误。

于 2013-08-06T21:06:06.620 回答
10

提示名称为“参数相关查找”。

它查找不合格的函数名称,这取决于arguments

它与查找参数无关

Bjarne 说错了。

于 2013-08-06T17:22:54.870 回答
8

我没有这本书,但这似乎是书中的一个错误,它缺少命名空间限定符的事实与 ADL 无关。应该是std::endl

于 2013-08-06T17:09:54.600 回答
4

是的,这是一个错误 - 该示例格式错误,不应编译。ADL 适用于引入函数调用表达式的非限定函数名。 endl是一个试图查找的 id 表达式std::endlendl不引入函数调用表达式,因此不使用依赖于参数的查找,仅使用非限定查找,因此不会std::endl按预期找到。

一个更简单和正确的例子是:

#include <vector>

int main()
{
    std::vector<int> x, y;
    swap(x,y); // calls std::swap due to ADL
}

总之,在查找f(x,y,z)具有非限定 id (eg) 的函数调用 (eg)之前,首先分析函数 (eg) 的参数以确定它们的类型。关联名称空间列表基于类型形成(例如,类型定义的封闭名称空间是关联名称空间)。然后在这些命名空间中额外搜索该函数。fx,y,z

Bjarne 示例的目的是展示std::operator<<函数的 ADL,而不是std::endl. 这需要额外了解重载运算符实际上是函数调用表达式,因此x << y意味着operator<<(x,y), 和operator<<是一个非限定名称,因此 ADL 适用于它。LHS 的类型是关联的命名空间,因此std::ostream可以找到。stdstd::operator<<(ostream&, ...)

更正后的注释应为:

如果没有依赖于参数的查找,将找不到命名空间<<中的重载运算符。std事实上,编译器注意到 << 的第一个参数是 std 中定义的 ostream。因此,它<<在 std 中查找运算符并找到它(in <iostream>)。

于 2013-08-07T16:17:37.603 回答