Сравнение std :: vector с использованием собственного класса в пространстве имен не компилируется - PullRequest
4 голосов
/ 04 июня 2019

Следующий код не компилируется, так как оператор сравнения не найден.

#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;
}

Может кто-нибудь объяснить причину такого поведения?

1 Ответ

7 голосов
/ 04 июня 2019

Это называется ADL, или поиск, зависящий от аргумента.

Для операторов компилятор будет искать не только в текущем пространстве имен подходящую функцию, но и в пространствах имен аргументов.

Например:

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

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

При вызове begin с помощью vec будет выполняться поиск в пространстве имен std, поскольку std::vector находится в этом пространстве имен. Для необработанного массива функция не найдена, поскольку у нее нет пространства имен, связанного с этим типом.

Один из способов отключить ADL - просто определить функцию:

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

Так же работает функция друга:

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 активируется. Затем найдите оператора в нужном пространстве имен и также можете найти дружественные функции.

Это позволяет использовать хороший синтаксис, а также позволяет универсальной функции вызывать функцию внутри вашего пространства имен.


Так почему же вектор не может найти тот в глобальном пространстве имен?

Это потому, что работает 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.

...