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

Ale*_*ph0 4 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;
}
Run Code Online (Sandbox Code Playgroud)

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

Gui*_*cot 7

这称为ADL或与参数有关的查找。

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

例如:

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

    auto b_vec = begin(vec); // std::begin
    auto b_arr = begin(arr); // Error!
}
Run Code Online (Sandbox Code Playgroud)

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

关闭ADL的一种方法是简单地限定功能:

// calls the std:: one, not the boost:: one
std::begin(some_boost_container);
Run Code Online (Sandbox Code Playgroud)

这也是朋友功能的工作方式:

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

find_me(3); // uh? no matching function?
Run Code Online (Sandbox Code Playgroud)

即使该功能非常好,也无法找到。

它需要在其参数中包含类的名称,以便ADL进入并在类范围内找到它:

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

find_me(test{}); // works!
Run Code Online (Sandbox Code Playgroud)

那么...对于运营商?为什么行得通?

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

// arg1 == arg2;
operator==(arg1, arg2);
Run Code Online (Sandbox Code Playgroud)

由于函数名称不合格,因此将使用ADL。然后在正确的名称空间中找到运算符,还可以找到朋友函数。

这既允许使用漂亮的语法,也允许泛型函数在名称空间内调用函数。


那么,为什么向量在全局名称空间中找不到那个向量?

这是因为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?
    }
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,ADL将搜索一个函数cant_you_find_me,但是如果无法通过ADL找到一个函数,则将不考虑全局名称空间,因为常规查找将改为查找最接近的函数:不带参数的函数。

这就是std名称空间的情况。它有很多operator==定义。如果ADL找不到合适的名称空间,则将不考虑全局名称空间,而是使用其中的名称空间std

  • @Ayxan之所以这么说是因为它可能根据类型调用成员函数或自由函数。 (2认同)