"std::transform" 在多个编译器上编译时没有限定名称查找?

"std::transform" compiles without qualified name lookup on multiple compilers?

当我发现对 std::transform 的调用在没有使用 std:: 命名空间前缀完全限定调用的情况下意外编译正常时,我正在用 C++ 编写一个库。根据 C++ 文档,std::transform 是在 std:: 命名空间中定义的,因此在没有完全限定名称的情况下不应编译。

#include <algorithm>
#include <vector>

int main()
{
    std::vector<int> v{ 1, 2, 3 };

    // "transform" is supposed to be in the namespace "std::"???
    transform(v.begin(), v.end(), v.begin(), [](const auto& value) {
        return value + 1;
        });
}

起初我认为这是微软标准库实现中的一个错误,因为我在 Visual Studio 工作,但后来我在 https://godbolt.org/ 上用一堆不同的编译器测试了它而且它似乎在其他编译器上也能正常编译!

gcc example

clang example

MSVC example

transform 调用更改为 ::transform 会导致编译失败,因此很明显全局命名空间中没有 transform 的符号。

我的猜测是,有人在 <algorithm> 头文件的某处不小心在全局命名空间中键入了 using std::transform;,这将使该函数可以在没有 std:: 前缀的情况下调用...但那会被认为是不好的做法,并且会成为标准库实现中的错误,不是吗?

我是不是刚刚在 multiple 编译器的标准库实现中发现了一个错误!?如果是这样,我什至会如何报告这样的错误?

不,这不是错误1。这是 argument dependent lookup(又名 Koenig 查找)。

当函数的参数位于特定命名空间中时,编译器将在该命名空间中查找函数本身。

在这种情况下,关系可能不明显,但是函数的参数是 v.begin() returns,类似于 std::vector<int>::iterator2 ,因此编译器也在名称空间 std 中查找该函数。


1. 然而,这是非常出乎意料的行为,一些人不仅认为这是语言定义中的错误。
2. 但这可能不是它的确切名称。 vector 具有相同的迭代器类型是很有用的,即使它们具有不同的分配器类型,并且要做到这一点,迭代器类型通常会在 vector 外部定义。