使用不带前缀 "std" 且不带 "using namespace std;" 的 std::sort() 编译成功

Using std::sort() without prefix "std" and also without "using namespace std;" compiles successfully

由于 sort()namespace std 中定义,因此必须始终用作 std::sort。但即使没有 std,以下代码也能正确编译。

#include <vector>
#include <algorithm>

int main()
{
    std::vector<int> nums = {4,3,1,7,2,0};
    sort(nums.begin(),nums.end());
}

ideone.com

但是这段代码没有。

#include <array>
#include <algorithm>

int main()
{

    std::array<int,5> nums = {4,1,8,9,6};
    sort(nums.begin(),nums.end());
}

使用 gcc 4.8.4 并启用 -std=c++11 标志。

从这两个代码片段中可以清楚地看出,std::vector 与 this.But 有关,我无法弄清楚。

这是依赖于参数的查找。根据 Stroustroup 的 C++ 编程语言:第 4 版,这里有两条规则适用:

1) If an argument is a member of a namespace, the associated namespaces are the enclosing namespaces.

2) If an argument is a built-in type, there are no associated namespaces.

在第一种和第二种情况下,begin() 和 end() return 迭代器。但是,C++ 标准将迭代器定义为任何类型的任何变量,可以对其执行迭代操作。 (换句话说,迭代器是一种通过 模板 强制执行的设计概念。)

根据另一个答案,第一种情况下的迭代器是数据类型的变量,属于与 sort() 相同的命名空间。但是,第二种情况下的迭代器具有原始数据类型。根据规则 #2,这些迭代器没有关联的命名空间。

这是argument-dependent lookup。如果使用typeid检查涉及的迭代器类型:

#include <iostream>
#include <typeinfo>
#include <vector>
#include <array>

int main() {
    std::cout << typeid(std::vector<int>::iterator).name() << '\n';
    std::cout << typeid(std::array<int, 5>::iterator).name() << std::endl;
    return 0;
}

至少在 Ideone,您会得到以下输出:

N9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE
Pi

在Revolver_Ocelot的帮助下,我们看到这些类型是__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >int*

对于向量,在通常的名称查找失败后,编译器会在 __gnu_cxxstd 命名空间中搜索 sort 函数,__gnu_cxx 因为它是命名空间__gnu_cxx::normal_iteratorstd 的名称空间,因为它是模板参数之一 std::vector<int, std::allocator<int> > 的命名空间。它找到 std::sort.

对于 std::array,迭代器只是 int*,因此依赖于参数的查找不会搜索其他名称空间,也不会找到 sort 函数。