std::vector<T> 的比较运算符无法找到 T 的比较运算符

14
下面这段非常简单的代码无法编译。
#include <vector>
#include <string>


namespace Foobar {
    struct Test {
        std::string f;
        std::uint16_t uuid;
    };
}

bool operator==(const Foobar::Test& lhs, const Foobar::Test& rhs){
    return lhs.f == rhs.f && lhs.uuid == rhs.uuid;
}


int main(){

    std::vector<Foobar::Test> a;
    std::vector<Foobar::Test> b;

    if(a==b){

    }

    return 0;
}

https://godbolt.org/g/zn6UgJ

我所拥有的编译器都无法编译。

以下代码会出现如下错误:

#include <vector>
#include <string>


namespace Foobar {
    struct Test {
        std::string f;
        std::uint16_t uuid;
    };

    bool operator==(const Foobar::Test& lhs, const Foobar::Test& rhs){
        return lhs.f == rhs.f && lhs.uuid == rhs.uuid;
    }
}



int main(){

    std::vector<Foobar::Test> a;
    std::vector<Foobar::Test> b;

    if(a==b){

    }

    return 0;
}

https://godbolt.org/g/o4pc1b

编译没有问题,这让我想到 std::vector<T> 的比较运算符会查找 T 的命名空间,为什么它不考虑全局命名空间呢?


@PasserBy 这并没有回答问题。Koenig查找添加了命名空间以进行查找,但问题是为什么全局命名空间中的函数不被考虑。 - SergeyA
@SergeyA 嗯...说得有道理。我猜想期望程序自动推断问题的其余部分可能有些过分了。但是你可以 有点儿 感受到为什么会发生这种情况,即使没有确切的细节。 - Passer By
我提名重新开放这个问题,因为重复问题并没有回答关于“为什么全局命名空间中的函数不被考虑”的问题。 - SergeyA
我在Qt的QVector中也看到了完全相同的行为。由于某些原因,编译器不喜欢当某个东西在向量中时,向量项的比较运算符在类外面被定义。我认为命名空间并不那么重要。重要的是你是在类内还是类外声明它。这可能是std::vector所需要的。 - santahopar
@SergeyA 有哪个 dup 被提名了? - curiousguy
@curiousguy 不记得完整的链接了,但问题是什么是ADL查找。 - SergeyA
1个回答

10

普通的未经资格认证的名称查找从使用该名称的上下文开始,并向上遍历包含它的封闭范围的链。它在包含匹配名称的最嵌套作用域停止。即使后来确定找到的名称不合适(例如,给定调用时函数重载不可行;或成员函数无法访问),这仍然是正确的。

在这里,查找的上下文是std::operator==(vector, vector),因此它从命名空间std开始查找。在命名空间std中有大量operator==的重载版本,因此普通查找会在那里停止,永远不会到达全局命名空间。

在第二个示例中,函数重载是通过参数相关的查找找到的。除了未经资格认证的查找之外,在函数调用中特别为函数名执行此查找,并在与调用参数类型相关联的范围内查找名称。在本例中,命名空间FoobarFoobar::Test相关联,因此参数相关的查找会搜索该命名空间并找到Foobar::operator==

因此,逻辑上属于类公共接口的自由函数 - 如重载运算符 - 通常应该在与类本身相同的命名空间中定义,以便让参数依赖查找有机会工作。 std::operator ==(vector,vector) 是一个很好的例子 - 在您的示例中,a == b 通过参数依赖查找工作。


我不理解“查找的上下文是std::operator==(vector, vector)[...]它开始在命名空间std中查找[...]因此普通查找就在那里停止”的部分。这个问题中的查找难道不是比较类型为T的向量元素的查找吗?为什么T == T的查找应该从std开始并因为找到了“大量重载”而停止呢?你能帮我解决一下吗? - lubgr
1
考虑 int i; void foo() { void* i; i = 42; }。通常的查找发生在 std::vector 的等号运算符定义中,即从 std 中进行查找。 - Passer By
1
@PasserBy 感谢您的解释,我明白了。但是为什么在找不到匹配项时它会停在 std 中呢?继续在外部范围搜索不是很直接吗? - lubgr
1
那么你的意思是 ::operator==(const Foobar::Test& lhs, const Foobar::Test& rhs)std::operator==(const vector<T>& lhs, const vector<T>& rhs) 隐藏了,对吗? - curiousguy
@curiousguy 是的。不仅仅是std::operator==(const vector<T>& lhs, const vector<T>& rhs) - 在命名空间std中有大量的operator==重载,每个重载都会在查找从std内部开始时隐藏::operator== - Igor Tandetnik
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接