为什么我必须写std :: cout而不是std :: <<


70

为什么我必须编写std::cout而不是std::<<像下面这样的代码行:

#include <iostream>

int main() {
    std::cout << "Hello, world!";
    return 0;
}

cout来自std库,<<通常不用于移位吗?那么,为什么我也不必::在之前也写作用域运算符<<,因为它也有其他含义?编译器如何知道在之后std::cout<<意味着另一件事?


22
基于参数的查找
RiaD

5
此行为也称为Koenig查找或ADL
P0W 2013年

9
<<顺便说一句,如果您要完全合格,则必须看起来像std::operator<<(std::cout, "Hello, world!");。C ++的语法不会让你写std::cout std::<< "Hello, world!"意味着同样的事情,因为命名空间的概念并不适用于运营商自己,它适用于那重载它们的函数的名称,这是operator<<
史蒂夫·杰索普

9
+1提出这个。这不是很多人自己想出的东西。
克里斯,

7
其他人已经提到了依赖于参数的查询(又名Koenig查询)。我认为值得补充的是,这正是安德鲁·科尼格(Andrew Koenig)首先发明了基于参数的查询的原始原因。它在其他地方出现,但是插入iostream(或从其中提取)是促使它开始的原因。
杰里·科芬

Answers:


55

首先,编译器将查看左侧和右侧的类型<<std::cout是类型的std::ostream,字符串文字是类型为15的数组const char。由于左侧是类类型,它将搜索名为的函数operator<<。问题是,它在哪里看?

此名称的查询operator<<是所谓的非限定查询,因为函数名称不像一样限定std::operator<<。对函数名称的不合格查找将调用与参数相关的查找。依赖于参数的查找将搜索与参数类型关联的类和名称空间。

当包含时<iostream>,签名的自由功能

template<typename traits>
std::basic_ostream<char, traits>& operator<<(std::basic_ostream<char, traits>&,
                                             const char*);

已在namespace中声明std。该名称空间与的类型相关联std::cout,因此将找到此函数。

std::ostream只是typedef的类型std::basic_ostream<char, std::char_traits<char>>,可以将15const char数组隐式转换为char const*(指向数组的第一个元素)。因此,可以使用两种参数类型来调用此函数。

还有其他重载operator<<,但我上面提到的函数与参数类型和本例中选择的参数最匹配。


一个与参数相关的查找的简单示例:

namespace my_namespace
{
    struct X {};

    void find_me(X) {}
}

int main()
{
    my_namespace::X x;
    find_me(x);       // finds my_namespace::find_me because of the argument type
}

注意:由于此函数是运算符,因此实际查找要复杂一些。通过在第一个参数(如果是类类型)的范围内进行合格查找来查找它,即作为成员函数。此外,将执行不合格的查找,但会忽略所有成员函数。结果略有不同,因为不合格的查找实际上就像一个两步过程,其中依赖于参数的查找是第二步。如果第一步找到成员函数,则不执行第二步,即使用依赖于参数的查找。

比较:

namespace my_namespace
{
    struct X
    {
        void find_me(X, int) {}
        void search();
    };
    void find_me(X, double) {}

    void X::search() {
        find_me(*this, 2.5); // only finds X::find_me(int)
        // pure unqualified lookup (1st step) finds the member function
        // argument-dependent lookup is not performed
    }
}

至:

namespace my_namespace
{
    struct X
    {
        void operator<<(int) {}
        void search();
    };
    void operator<<(X, double) {}

    void X::search() {
        *this << 2.5; // find both because both steps are always performed
        // and overload resolution selects the free function
    }
}

6
作为计算机科学家,这是正确的答案。作为工程师,我有一个简短的答案:“通过设计。命名空间的设计要求有限的额外限定条件。足以消除歧义,但不超过此。”
MSalters 2013年

1
顺便说一句,可以通过将函数名称放在括号中来禁用ADL (find_me)(x)。我不知道是否/如何适用于运算符语法。
Matthias 2013年

@Matthias有趣。对于操作员,您可以停止不合格的查找,例如通过使用声明(仍然找到成员函数),也可以使用显式语法(operator<<)(std::cout, "Hello, world!")现场示例
dyp

9

std::cout << "Hello, world!"; //calls std:::operator <<

这是通过依赖于参数的名称查找(ADL,又名Koenig查找)来实现的。

虽然我们只有一个std限定符,但是std名称空间有两件事

  • cout
  • <<

没有ADL,(Koenig查找)

std::cout std:: << "Hello World" ;//this won't compile

为了编译它,我们需要使用更丑陋的形式

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

因此,为了避免这种丑陋的语法,我们必须感谢Koenig Lookup :)


3

编译器认为<<的参数是std :: ostream对象和字符串,因此可以基于此找到正确的operator <<定义。

您可以将运算符(或实际上是任何函数)的参数类型视为其名称的一部分。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.