Объявление дружественной функции C ++ 17 и встроенное пространство имен

Рассмотрим следующую программу

#include <iostream>

namespace N1
{
    inline namespace N2
    {
        class A
        {
        public:
            friend void f( const A & );
        private:
            int x = 10;
        };

        //void f( const A & );
    }

    void N2::f( const A &a ) { std::cout << a.x << '\n'; }
}

int main()
{
    using namespace N1;

    A a;

    f( a );
}    

В соответствии со стандартом C ++ 17 (определения членов пространства имен 10.3.1.2)

3 Если объявление друга в нелокальном классе сначала объявляет класс, функцию, шаблон класса или шаблон функции97, то друг является членом самого внутреннего включающего пространства имен. Объявление друга само по себе не делает имя видимым для неквалифицированного поиска (6.4.1) или квалифицированного поиска (6.4.3).

Таким образом, имя функции друга невидимо в пространстве имен N2. Так что он также должен быть невидимым в пространстве имен N1.

Однако код компилируется и выполняется без предупреждений clang HEAD 10.0.0.

Компилятор gcc HEAD 10.0.0 20191 выдает предупреждения

prog.cc:18:10: warning: 'void N1::N2::f(const N1::N2::A&)' has not been declared within 'N1::N2'
   18 |     void N2::f( const A &a ) { std::cout << a.x << '\n'; }
      |          ^~
prog.cc:10:25: note: only here as a 'friend'
   10 |             friend void f( const A & );
      |                         ^

но запускает программу и выводит правильный результат.

Visual C ++ 2019 также успешно компилирует код без предупреждений, и программа выводит ожидаемый результат.

Есть ли ошибка трех компиляторов, потому что имя f невидимо, поэтому определение функции друга f во включающем пространстве имен неверно?


person Vlad from Moscow    schedule 17.12.2019    source источник
comment
Как это незаметно? Вы это четко определили, и это видно.   -  person Öö Tiib    schedule 18.12.2019
comment
@ ÖöTiib См. Цитату из стандарта C ++.   -  person Vlad from Moscow    schedule 18.12.2019
comment
Где сказано, что определение функции становится невидимым?   -  person Öö Tiib    schedule 18.12.2019
comment
@ ÖöTiib Какая функция ?! Имя f невидимо в пространстве имен N2.   -  person Vlad from Moscow    schedule 18.12.2019
comment
Несмотря на то, что N2 :: f четко определен в опубликованном коде?   -  person Öö Tiib    schedule 18.12.2019
comment
GCC действительно генерирует серьезную ошибку, когда вы используете -pedantic-errors, поэтому, похоже, предполагается, что код неправильно сформирован в соответствии со стандартом. Ошибка связана с определением, поэтому тело main на самом деле не имеет значения. И inline тоже. Без него все компиляторы ведут себя одинаково.   -  person walnut    schedule 18.12.2019
comment
@ ÖöTiib Еще один. Имя f невидимо в пространстве имен N2. Итак, как можно определить функцию во включающем пространстве имен N1, если поиск по квалифицированному имени не находит функцию?   -  person Vlad from Moscow    schedule 18.12.2019
comment
Параграф 2 также кажется очень актуальным.   -  person aschepler    schedule 18.12.2019
comment
@aschepler Итак, два абзаца сбивают с толку.   -  person Vlad from Moscow    schedule 18.12.2019
comment
Функция, впервые объявленная в объявлении друга, имеет связь с пространством имен, членом которого она является. Таким образом, функция может быть сначала объявлена ​​объявлением друга, и поэтому внешнее определение соответствует ей и не может жаловаться на отсутствие действительных объявлений и делает ее видимой.   -  person Öö Tiib    schedule 18.12.2019
comment
@ ÖöTiib Было бы правильно, если бы функция была объявлена ​​также вне определения класса в пространстве имен N2.   -  person Vlad from Moscow    schedule 18.12.2019
comment
Так вы утверждаете, что определение N1 :: N2 :: f неверно?   -  person Öö Tiib    schedule 18.12.2019
comment
@ ÖöTiib Думаю, да. Также учтите, что компиляторы ведут себя по-разному.   -  person Vlad from Moscow    schedule 18.12.2019
comment
Для меня это предыдущее объявление, и где говорится, что определение должно выполнять поиск?   -  person Öö Tiib    schedule 18.12.2019
comment
@ ÖöTiib Поскольку используется полное имя, объявленное в другом пространстве имен.   -  person Vlad from Moscow    schedule 18.12.2019


Ответы (1)


Как я упоминал в своем комментарии, тела функций и параметр f на самом деле не актуальны, поскольку проблема только в поиске имени N2::f. После их удаления также не имеет значения, является ли пространство имен N2 inline или нет. В любом случае все компиляторы ведут себя одинаково.

GCC предупреждает, но выдает серьезную ошибку с -pedantic-errors в определении N2::f. MSVC и Clang всегда принимают код без диагностики.

Я думаю, вы правы, что, следуя формулировке N2::f в деклараторе определения функции, следует искать правила поиска по квалифицированному имени, которые не должны находить объявление friend без промежуточного объявления f в области N2, без использования квалифицированного имени.

Однако есть отчет о дефектах 1477, который кажется иметь намерение сделать такое определение вне пространства имен правильно сформированным.

В открытом выпуске CWG 1900 этот вопрос вынесен, и в описании проблемы также делается вывод о том, что нормативный текст стандарта не допускает определение. Он также отмечает, что, как вы наблюдаете, существует расхождение в реализации.

Для Clang отчет об ошибке аналогичен здесь.

person walnut    schedule 17.12.2019
comment
Имеет значение, что пространство имен встроено, потому что в противном случае неквалифицированное имя A не будет найдено в демонстрационной программе. - person Vlad from Moscow; 18.12.2019
comment
@VladfromMoscow Да вы правы. Кажется, я удалил содержимое тел функций и параметра и забыл об этом. Дело в том, что компиляторы ведут себя одинаково в отношении поиска имени для N2::f, независимо от того, является ли пространство имен встроенным. - person walnut; 18.12.2019