C ++ встроенное определение функции друга - PullRequest
6 голосов
/ 23 марта 2019

В текущем проекте стандарта C ++ (март 2019 г.) [class.friend] стр.6 состояния (выделено мной):

Функция может быть определена в объявлении друга класса, если и только если класс является нелокальным классом ([class.local]), имя функции неквалифицированное, и функция имеет область пространства имен . [...]

Что означает "функция имеет область имен"?

Единственная ситуация, в которой я мог бы придумать, в которой функция не имеет области видимости, это следующая:

struct A
{
    static void f();

    struct B
    {
        friend void f() {}; 
    }; 
};

Однако и clang и gcc не связывают определение друга внутри B со статическим методом внутри A, но с функцией, которая принадлежит глобальному пространству имен.

Есть ли другие ситуации, по которым я скучаю?

Ответы [ 2 ]

3 голосов
/ 23 марта 2019

Я думаю, что вы на самом деле ответили на свой вопрос, но не осознавая этого.

«Функция имеет область имен» означает, что она является частью пространства имен, а не частью класса или структуры. Таким образом, функция A :: B :: f () не существует. И это также не относится к A :: f (). Вместо этого функция, которую вы определили как друга, на самом деле является функцией :: f (), поскольку это пространство имен, в котором она находится (глобальное пространство имен).

Я подозреваю (но не пытался), что если вы поместите все это в пространство имен, то определяемая вами функция f () будет частью этого пространства имен. Так, например,

namespace ns {
    struct A
    {
        static void f();

        struct B
        {
            friend void f() {}; 
        }; 
    };
}

определит функцию друга как функцию ns :: f ().

0 голосов
/ 23 марта 2019

В поисках соответствующего правила, которое гласит, что friend определение неквалифицированного имени функции всегда является членом пространства имен, я обнаружил, что это не совсем так. [Namespace.memdef] / 3:

Если объявление friend в нелокальном классе сначала объявляет класс, функцию, шаблон класса или шаблон функции, то друг является членом внутреннего вложенного пространства имен. ... Если имя в объявлении friend не является ни квалифицированным, ни template-id , и объявление является функцией или подробный спецификатор типа , поиск для определения независимо от того, была ли ранее объявлена ​​сущность, она не должна рассматривать какие-либо области за пределами самого внутреннего вмещающего пространства имен.

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

Таким образом, формулировка, похоже, имеет значение в этом коде:

struct A {
    template <typename T>
    static void f();

    template <typename T>
    struct B {
        friend void f<T>() {}
    };
};

Здесь в объявлении друга указано имя template-id , поэтому правило о пропуске областей, не относящихся к пространству имен, не применяется, и f действительно называет шаблон функции A::f. Поэтому [class.friend] / 6 говорит, что это неправильно, хотя, если бы объявление friend не было определением, оно было бы правильно сформировано.

...