为什么我必须写std::cout
而不是std::<<
像这样的一行代码:
#include <iostream>
int main() {
std::cout << "Hello, world!";
return 0;
}
cout
来自std
图书馆,<<
通常不用于进行位移?那么,为什么我不必在::
之前也写范围运算符<<
,因为它还具有另一种含义?编译器如何知道 , 之后std::cout
意味着<<
另一件事?
为什么我必须写std::cout
而不是std::<<
像这样的一行代码:
#include <iostream>
int main() {
std::cout << "Hello, world!";
return 0;
}
cout
来自std
图书馆,<<
通常不用于进行位移?那么,为什么我不必在::
之前也写范围运算符<<
,因为它还具有另一种含义?编译器如何知道 , 之后std::cout
意味着<<
另一件事?
首先,编译器会查看<<
. std::cout
是 type 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*);
已在命名空间中声明std
。此命名空间与 的类型相关联std::cout
,因此将找到此函数。
std::ostream
只是一个 typedef std::basic_ostream<char, std::char_traits<char>>
,并且15 的数组const char
可以隐式转换为 a 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
}
NB 由于这个函数是一个操作符,实际的查找有点复杂。它是通过在第一个参数(如果它是类类型)范围内的限定查找来查找的,即作为成员函数。此外,执行非限定查找,但忽略所有成员函数。结果略有不同,因为非限定查找实际上就像一个两步过程,其中依赖于参数的查找是第二步。如果第一步找到成员函数,则不执行第二步,即不使用参数相关查找。
相比:
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
}
}
在std::cout << "Hello, world!"; //calls std:::operator <<
这是通过依赖于参数的名称查找(ADL,又名Koenig Lookup)来实现的
虽然我们只有一个限定符,但命名空间std
有两件事std
cout
<<
没有 ADL,(Koenig 查找)
std::cout std:: << "Hello World" ;//this won't compile
为了编译它,我们需要使用更丑的形式
std::operator<<(std::cout, "Hello, world!");
所以为了避免这种丑陋的语法,我们必须欣赏 Koenig Lookup :)
编译器看到 << 的参数是一个 std::ostream 对象和一个字符串,因此能够基于此定位正确的 operator<< 定义。
您可以将运算符(或实际上任何函数)的参数类型视为其名称的一部分。