- 李嘉轩 的博客
郑智允大佬的教程 2 --- C++ 的 ADL 机制
- 2024-8-4 23:05:21 @
返回
下面简称郑智允为ZZY,冯建鑫为FJX
1(FROM ZZY)
#include <iostream>
int main()
{
endl(std::cout);
return 0;
}
猜这段代码能否通过编译。
如果不能,是因为什么编译错误?
如果能,请解释原因。
2(FROM FJX)
能。
首先,endl是一个操作符,由于他在<<后,所以它应该是一个参数。其实不然,它是一个函数,内部函数就说明了这一点:
ostream& ostream::operator << ( ostream& (*op) (ostream&))
{
// call the function passed as parameter with this stream as the argument
return (*op) (*this);
}
std::ostream& std::endl (std::ostream& strm)
{
// write newline
strm.put('\n');
// flush the output buffer
strm.flush();
// return strm to allow chaining
return strm;
}
很明显,endl是一个函数,再由于你没有明确的命名空间,所以参数你填了std::cout。(如果有using namespace std
,那就填cout)
3(FROM ZZY)
问题出现在这里,为什么不是 std::endl 而仅是 endl 就可以编译通过呢?
4(FROM FJX)
endl是一个函数,我都说了……
5(FROM ZZY)
函数也需要限定引用啊?
6(FROM ZZY)
endl 声明在 标准命名空间中,这里凭什么就可以不指明是标准命名空间?
7(FROM ZZY)
照你所说,以下的 std::next 也是一个函数,为什么不加 std:: 限定引用反而会编译失败?
#include <algorithm>
#include <iostream>
int main()
{
int a[3] = {1, 2, 3};
//std::cout << *next(a, 1); // 编译不通过
std::cout << *std::next(a, 1);
return 0;
}
8(FROM FJX)
endl确实在std中,但他的函数在内部库中,跟标准命名空间没有一毛钱关系!
9(FROM ZZY)
endl 就是一个函数。
10(FROM ZZY)
而你所说的内部库,其实是头文件。根据标准,std::endl 必须声明在且仅在 标准命名空间 中。
11(FROM ZZY)
换言之,你无法在 标准命名空间 以外的任何地方找到 C++ 自带的 endl 函数。
12(FROM ZZY)
endl 并没有分成两部分实现。endl 只有一个,在 标准命名空间中。
13(FROM FJX)
#include <algorithm>
#include <iostream>
int main()
{
int a[3] = {1, 2, 3};
//std::cout << *next(a, 1); // 编译不通过
std::cout << *std::next(a, 1);
return 0;
}
你说的是*std::next,*next当然不通过
14(FROM ZZY)
如果他的函数在内部库当中,那么如果要让以上代码编译通过,我们一定需要一种方式来得知一个内部库的 endl 的声明,问题就在于你无法找到 endl 除了在标准命名空间以外的第二个声明。
15(FROM FJX)
你没有using namespace std
16(FROM ZZY)
但是如果是这样就可以编译通过了:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> arr{1, 2, 3};
std::cout << *next(arr.begin(), 1) << '\n';
return 0;
}
17(FROM ZZY)
你有应该如何解释这种情况呢?
18(FROM FJX)
所以你要加std::
19(FROM ZZY)
我并没有给 next 加 std::,仅仅是把数组变成了 std::vector 而已。
20(FROM FJX)
你加别的库了,兼容了标准命名空间
21(FROM ZZY)
让我换一种毫无争论的方式来提问吧。
22(FROM ZZY)
见如下代码,假定你已经知道模板等知识:
#include <cstdlib>
#include <iostream>
namespace A
{
struct S
{};
void func(S x)
{
exit(0);
}
} // namespace A
namespace B
{
// 调用一个没有实现的函数,编译会出现链接错误 (1)
template <typename T>
void func(T x);
} // namespace B
int main()
{
using namespace B;
A::S value;
func(value); // 但这里却不会报错,甚至能够运行
// func(2); // 如 (1) 处所言,这里会因为找不到定义而编译错误
return 0;
}
23(FROM ZZY)
注意,我在主函数中 using namespace 的是 B 命名空间。
24(FROM ZZY)
@ 郑智允 你加别的库了,兼容了标准命名空间
或者我全部换成 万能头,之前代码的效果依然不变。
#include <bits/stdc++.h>
int main()
{
int a[3] = {1, 2, 3};
//std::cout << *next(a, 1); // 编译不通过
std::cout << *std::next(a, 1);
return 0;
}
#include <bits/stdc++.h>
int main()
{
std::vector<int> arr{1, 2, 3};
std::cout << *next(arr.begin(), 1) << '\n';
return 0;
}
25(FROM ZZY)
帖子不能烂尾,简单收一下尾。
这是由于 C++ 存在一种叫 ADL 的机制,具体见 这里。