为什么在语句“std::cout << std::endl;”中使用参数依赖查找时,“std::endl”需要命名空间限定符?

7

我在浏览关于ADL(依赖参数名称查找)的维基百科条目时,发现以下示例(2014年1月4日):

#include<iostream>

int main() 
{
  std::cout << "Hello World, where did operator<<() come from?" << std::endl;
}

...带有以下注释:

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

我的想法是,这个注释是不正确的(或者只是不清楚)。我正在考虑改变注释,以便说:

请注意,std::endl需要完全限定, 因为ADL不适用于函数调用的参数;它仅适用于函数名称本身。

我是否正确地理解了维基百科的注释?我的修改是否正确?(即,在这个例子中,我对ADL的理解是否正确?)


同样的原因,为什么 cout 需要命名空间限定。 - user541686
@Mehrdad 虽然可以想象,如果 ADL 用于查找作为函数的其他参数使用的名称,则可以使用 ADL 找到该名称。 - Dan Nissenbaum
ADL的意思是函数的名称可以从参数中推断出来,而不是反过来。 - Kerrek SB
@KerrekSB 还有第三种可能的情况 - 即可以从某个参数推断出其他参数的名称。我(认为我)知道这不是ADL的工作方式;因此我建议编辑维基百科文章。 - Dan Nissenbaum
@DanNissenbaum:您为什么会这样认为?我认为[basic.lookup.argdep]非常清晰:“可以搜索其他命名空间[用于后缀表达式的名称……]。这些对搜索的修改取决于参数的类型”。 - Kerrek SB
@KerrekSB 或许这需要另一个问题,但是 namespace Foo { class A{}; class B{}; void f(A, B){} } 接着 Foo::A a; f(a, B()); 是否正确地定位了名称 B?(如果我理解正确的话,这将是ADL在命名空间内成功定位函数调用的参数类型,给定另一个已知类型定义在该命名空间中的函数参数。) - Dan Nissenbaum
3个回答

10

维基百科所说的完全没有问题。

std::cout << "Hello World, where did operator<<() come from?" << std::endl

等价于以下内容(假设operator<<已经作为自由函数实现)

operator<<(
    operator<<(std::cout, "Hello World, where did operator<<() come from?"),
    std::endl)

由于这是参数依赖查找(函数的),而不是"参数查找",因此明显需要对coutendl都进行命名空间限定。
参数决定要调用的函数,而不是反过来。


好观点。当展开嵌套时,可以清楚地看到std::endl是第一个调用operator<<namespace std中的唯一参数,因此需要触发ADL。然而:难道维基百科中的注释(std::endl是函数指针,而不是函数调用)不是无关紧要的(因此具有误导性)吗? - Dan Nissenbaum
@DanNissenbaum: 我想我可以同意,这可能会有点误导,因为如果有一个 std :: get_endl() 函数返回了 std::endl,那么它将是一个函数调用,但这与整个 ADL 问题无关。这是一种迂回的思考方式,所以不常见将其解释为这样,但如果您这样做,我想这确实令人困惑。您所说的肯定没有错,所以也许可以继续更改? - user541686
@DanNissenbaum:我认为Wikipedia的意图是反驳可能的争论,即“但‘endl(std :: cout)’不需要命名空间限定!”对此的答案是“这里的' endl '是一个函数调用,而不是一个函数指针”。 在那个情境下有道理,但如果你字面上阅读它,我可以理解为什么会误导人。 - user541686

3

原始措辞和您的措辞都是正确的。

std::endl一个函数。参见C++03规范第27.6节[lib.iostream.format]

头文件<ostream>概要

namespace std {
template <class charT, class traits = char_traits<charT> >
class basic_ostream;
typedef basic_ostream<char> ostream;
typedef basic_ostream<wchar_t> wostream;
template <class charT, class traits>
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os);
...

在这种情况下,std::endl函数(更准确地说,它所衰变成的函数指针)被作为参数传递给operator<<。由于它是一个参数,因此ADL不适用。

如果是这样,我认为维基百科上的该语句:“(std::endl是一个函数指针,而不是一个函数调用)”与ADL无关,因此具有误导性。我想删除它。 - Dan Nissenbaum
从技术上讲,这并没有错...但我同意它似乎是无关紧要的,并且可能会引起误导。所以我会说去做吧。 - Nemo

1
维基百科的条目是正确的。事实上,operator<<的一个操作数在std命名空间中,这导致了名称查找将std命名空间中的operator<<声明包含在提供给重载决议的候选声明集中。

请注意,我询问的不是查找函数声明,而是查找函数的另一个参数。换句话说,因为至少有一个参数在namespace std中,ADL可以被用来查找该命名空间中其他参数的位置,以定位函数。 - Dan Nissenbaum
@DanNissenbaum 这将是关于参数名称的函数相关查找,而不是关于函数名称的参数相关查找。 - jthill
因此,根据这个推理,我认为我的建议更改是正确的。也许我没理解。 - Dan Nissenbaum
@DanNissenbaum 我认为你修改后的版本和维基百科的版本都是正确的。如果我想改进它,我会重申我之前所说的话,即搜索所有提供参数的命名空间以查找重载候选项(也许还可以添加一句话,由于没有为 endl 提供参数,因此需要限定符或使用)。 - jthill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接