C ++ не может найти функцию вне пространства имен

Компиляция следующего кода завершается неудачно, потому что вторая функция не может найти первую, даже если она находится вне пространств имен. Сам не мог разобраться в проблеме и пока не нашел ответов в сети.

test.cpp:

#include <bits/stdc++.h>

struct myclass {};

template <typename T, typename U>
std::ostream& operator<< (std::ostream &os, const std::pair<T, U> &p) {
    os << "(" << p.first << ", " << p.second << ")";
    return os;
}

namespace my {
    void operator<< (std::ostream os, myclass m) {
        std::cout << std::pair<int, int>(5, 4); // This is line 13.
    }
}

int main() {
    return 0;
}

Ошибка компилятора (g++ test.cpp -O2 -o test.exe):
test.cpp:13:13: error: no match for 'operator<<' (operand types are 'std::ostream {aka std::basic_ostream<char>}' and 'std::pair<int, int>').
И продолжается, дается длинный список предложений относительно того, что могло означать operator<<.

Наблюдение 1: Если две функции различаются по имени, ошибки не возникает.
Наблюдение 2: Если namespace my { } удаляется, ошибки не возникает.


person hadizadeh.ali    schedule 22.02.2019    source источник
comment
related / dupe: stackoverflow.com / questions / 25133383 /   -  person NathanOliver    schedule 22.02.2019
comment
Явно вызывая глобальный оператор: ::operator<<(std::cout, std::pair<int, int>(5, 4));, он должен работать, но это ужасно некрасиво (и не имеет цепочки). Я не буду публиковать это в качестве ответа, если есть способ получше.   -  person scohe001    schedule 22.02.2019
comment
Лучше всего использовать директиву using перед вызовом (using ::operator<<;). Что касается того, почему поиск не приводит к обнаружению canidate в глобальном пространстве имен ...   -  person Rerito    schedule 22.02.2019


Ответы (2)


Это своего рода сокрытие имени; функции / операторы не могут быть перегружены из-за разных областей видимости.

Согласно правилу поиска имени,

(курсив мой)

... поиск по имени проверяет области, как описано ниже, до тех пор, пока не будет найдено хотя бы одно объявление любого типа, после чего поиск прекращается и другие области не проверяются.

В этом случае имя operator<< находится в области пространства имен my (то есть само), затем поиск имени останавливается, глобальная область не проверяется, глобальная operator<< не будет рассматриваться для следующего разрешения перегрузки.

А также

Наблюдение 1: Если две функции различаются по имени, ошибки не возникает.

Это нормально, потому что имя не скрывается.

Наблюдение 2: Если пространство имен my {} удаляется, ошибки не возникает.

Это нормально, потому что два operator<< помещаются в одну и ту же область видимости, то есть в глобальное пространство имен. Тогда оба operator<< могут быть найдены и затем рассмотрены при разрешении перегрузки, в конце концов будет выбран подходящий.

Как было предложено в комментариях, вы можете применить using, чтобы ввести имена из глобального пространства имен в пространство имен my; тогда оба operator<< будут найдены и рассмотрены при разрешении перегрузки.

person songyuanyao    schedule 22.02.2019

operator<<, определенный в namespace my, предотвращает разрешение перегрузки с учетом вашего operator<<, определенного в глобальном пространстве имен. Здесь вы можете полагаться только на ADL или вносить желаемую перегрузку в текущую область:

namespace my {

std::ostream& operator<<(std::ostream& os, myclass m) {
    // Bringing the operator in the global namespace in the current scope
    using ::operator<<;
    std::cout << std::pair<int, int>(5, 4);
}

}
person Rerito    schedule 22.02.2019
comment
или более явно: ::operator<<(std::cout, std::pair<int, int>{ 5, 4 }); - person Const; 22.02.2019
comment
Я предпочитаю использовать решение, поскольку оно все еще позволяет ADL - person Rerito; 23.02.2019