Перегрузка функции вне класса не видна - PullRequest
1 голос
/ 22 апреля 2020

Рассмотрим следующий фрагмент кода:

enum class Bar {
    A
};

void foo(Bar) {}

struct Baz {
    void foo() {
        foo(Bar::A);
    }
};

Не удается скомпилировать, сообщение от g cc 9.2:

:12:19: error: no matching function for call to 'Baz::foo(Bar)'
 12 |         foo(Bar::A);
    |              

Я не подозреваю, что это ошибка так как лязг 10 тоже не получается. У меня есть два вопроса относительно этой ситуации:

  1. Где стандарт определяет поведение для таких перегрузок?

  2. Каковы возможные причины поведения компилятора? указан таким образом?

живой пример

Ответы [ 2 ]

6 голосов
/ 22 апреля 2020

Вызов foo внутри Baz::foo() будет искать только имена внутри класса. Если вы хотите использовать foo, объявленный вне класса Baz, вам необходимо использовать оператор разрешения области действия, например:

enum class Bar {
    A
};

void foo(Bar) {}

struct Baz {
    void foo() {
        ::foo(Bar::A);   // looks up global 'foo'
    }
};

Обратите внимание, что вызов unscoped для foo завершается неудачно, потому что Bar::foo находится в ближайшей области видимости. Если вы называете функцию по-другому, то в Bar функция не найдена, и компилятор будет искать эту функцию во внешней области видимости.

enum class Bar {
    A
};

void foo(Bar) {}

struct Baz {
    void goo() {   // not 'foo'
        foo(Bar::A);  // this is fine, since there is no 'Bar::foo' to find
    }
};

Вот цитата из cppreference для определения класса .

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

Конечно, это относится только к определениям классов, но для функций-членов (это ваш пример), оно говорит:

Для имени используемый внутри тела функции-члена, аргумента по умолчанию для функции-члена, спецификации исключений для функции-члена или инициализатора члена по умолчанию, области поиска такие же, как в [определении класса], ...

То же самое относится c.

5 голосов
/ 22 апреля 2020

В соответствии с правилом поиска безусловного имени, из стандарта, [basi c .lookup.unqual] / 1 ,

(выделение мое)

Во всех случаях, перечисленных в [basi c .lookup.unqual], в областях ищется объявление в порядке, указанном в каждой из соответствующих категорий; поиск имени заканчивается, как только найдено объявление для имени .

Это означает, что имя foo найдено в области видимости класса (то есть в самом Baz::foo) , тогда поиск имени останавливается; глобальный не будет найден и рассмотрен для разрешения перегрузки, которое произойдет позже.

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

struct Baz {
    void foo(int i) { }
    void foo() {
        foo('A');
    }
};

Вы знаете, 'A' будет преобразовано в int, а затем передано в foo(int), это нормально. Если функции могут быть перегружены через области, если когда-нибудь кто-нибудь или библиотека добавят в глобальную область видимости foo(char), поведение кода изменится, что довольно запутанно, особенно если вы не знаете о добавлении глобальной .

...