c++ - 比较 std::vector 在命名空间中使用自己的类不编译

标签 c++ stl namespaces operator-overloading stdvector

以下代码无法编译,因为未找到比较运算符。

#include <vector>
#include <iostream>
#include <string>

namespace Cool {
    struct Person {
        std::string name;
    };
}

bool operator==(const Cool::Person&  p1, const Cool::Person& p2) {
    return p1.name == p2.name;
}

int main(int, char *[])
{
    std::vector<Cool::Person> a{ {"test"} };
    std::vector<Cool::Person> b{ {"test"} };
    bool ok = a == b;
    std::cout << ok << std::endl;
}

经过一些实验,我发现以下代码可以完美编译:

#include <vector>
#include <iostream>
#include <string>

namespace Cool {
    struct Person {
        std::string name;
    };

    bool operator==(const Person&  p1, const Person& p2) {
        return p1.name == p2.name;
    }
}

int main(int, char *[])
{
    std::vector<Cool::Person> a{ {"test"} };
    std::vector<Cool::Person> b{ {"test"} };
    bool ok = a == b;
    std::cout << ok << std::endl;
}

有人可以解释这种行为背后的基本原理吗?

最佳答案

这称为 ADL,或参数依赖查找。

对于运算符,编译器不仅会在当前命名空间中搜索合适的函数,还会在参数的命名空间中搜索。

例如:

int main() {
    int arr[3] = {};
    std::vector<int> vec(3);

    auto b_vec = begin(vec); // std::begin
    auto b_arr = begin(arr); // Error!
}

当使用 vec 调用 begin 时,它将在 std 命名空间中搜索,因为 std::vector 位于该命名空间中。对于原始数组,找不到该函数,因为它没有与该类型关联的 namespace 。

关闭 ADL 的一种方法是简单地限定函数:

// calls the std:: one, not the boost:: one
std::begin(some_boost_container);

这也是 friend 函数的工作原理:

struct test {
    friend void find_me(int) {}
};

find_me(3); // uh? no matching function?

虽然功能很好,但是找不到。

它的参数中需要类的名称,以便 ADL 启动并在类范围内找到它:

struct test {
    friend void find_me(test const&) {}
};

find_me(test{}); // works!

那么...对于运营商?为什么它有效?

因为调用用户定义的运算符大致等同于:

// arg1 == arg2;
operator==(arg1, arg2);

由于函数名不合格,ADL 启动。然后在正确的命名空间中找到运算符,也可以找到友元函数。

这既允许良好的语法,也允许通用函数调用您的命名空间内的函数。


那么为什么vector在全局命名空间中找不到呢?

这是因为 ADL 的工作原理。在带有名称的函数的命名空间内,ADL 将仅在参数的命名空间内搜索。但如果找不到,它将回退到正常查找。

namespace Cool {
    struct Person {
        std::string name;
    };
}

bool cant_you_find_me(Cool::Person const& p);

namespace test {
    void cant_you_find_me();

    template<typename T>
    void test_template(T const& p) {
        cant_you_find_me(p); // ADL?
    }
}

在此示例中,ADL 将搜索函数 cant_you_find_me,但如果无法通过 ADL 找到一个函数,则不会考虑全局命名空间,因为正常查找将找到最接近的一个:那个不接受任何争论。

这就是 std 命名空间所发生的情况。它定义了很多operator==。如果 ADL 没有找到合适的,则不会考虑全局命名空间,而是考虑 std 中的命名空间。

关于c++ - 比较 std::vector 在命名空间中使用自己的类不编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56443955/

相关文章:

c++ - 删除元素时使用 STL 映射的迭代器时遇到问题

c++ - 从文件中读入时如何阻止空格显示为回车?

PHP fatal error : I'm trying to implement a Form\AbstractType in my Symfony2 application

c# - 具有相同结构的命名空间混淆

c++ - 建议的堆栈分配最大大小

c++ - 如何从 Node.js 调用 C++ 代码?

c++ - 如何从 STL 的 const_iterator 中获取数据?

c++ - 在 namespace 中组织变量和方法的优缺点

c++ - dlopen 是否创建多个库实例?

c++ - 返回类型自动扣除的好友函数模板无法访问私有(private)成员