Есть некоторые подробности, которые я не понял в §7.3.1.2/3 в стандарте С++ 11

§7.3.1.2/3 в стандарте С++ 11 (акцент мой):

Каждое имя first, объявленное в пространстве имен, является членом этого   Пространство имен. Если объявление друга в нелокальном классе first объявляет   класс или функция, класс или функция друга является членом   внутреннее пространство имен. Имя друга не найдено   безоговорочный поиск (3.4.1) или квалифицированный поиск (3.4.3), пока   в этой области пространства имен (или   до или после определения класса, дающего дружбу). Если друг   вызывается функция, ее имя может быть найдено по имени, которое   рассматривает функции из пространств имен и классов, связанных с   типы аргументов функции (3.4.2). Если имя в другом   объявление не является ни квалифицированным, ни идентификатором шаблона, а декларация   это функция или специфицированный тип-спецификатор, поиск для определения   был ли объект ранее объявлен, не рассматривает   области вне самого внутреннего охватывающего пространства имен. [Примечание: другое   формы объявлений друзей не могут объявить нового члена   внутреннее пространство имен и, следовательно, следовать обычным правилам поиска.

Пример:

// Assume f and g have not yet been defined.
void h(int);
template <class T> void f2(T);
namespace A {
    class X {
        friend void f(X); // A::f(X) is a friend
        class Y {
            friend void g(); // A::g is a friend
            friend void h(int); // A::h is a friend
            // ::h not considered
           friend void f2<>(int); // ::f2<>(int) is a friend
        };
    };
    // A::f, A::g and A::h are not visible here
    X x;
    void g() { f(x); } // definition of A::g
    void f(X) { /* ... */} // definition of A::f
    void h(int) { /* ... */ } // definition of A::h
    // A::f, A::g and A::h are visible here and known to be friends
}
using A::x;
void h() {
    A::f(x);
    A::X::f(x); // error: f is not a member of A::X
    A::X::Y::g(); // error: g is not a member of A::X::Y
}

Если мне что-то не хватает, я не понимаю необходимости в словах first выше. Насколько я могу судить, вы не можете иметь более одного объявления какого-либо объекта в пространстве имен и не более одного объявления функции друга в классе.

Кроме того, какова актуальность комментария "Предположим, что f и g еще не определены" в примере? На самом деле не имеет значения, объявлены ли эти функции перед определением пространства имен A. Они обязательно будут принадлежать глобальному пространству имен, и они не будут иметь ничего общего с функциями, объявленными внутри пространства имен A.

Edit:

Тот факт, что можно повторять декларации одной и той же функции или декларации и определения функции в пространстве имен, не отменяет моего наблюдения, что использование слов first в §7.3.1.2/3 не нужны.

Edit1

Я только что нашел еще одну ошибку. Комментарий ::f2<>(int) is a friend неверен. Не только определение функции шаблона f2(T) в пространстве имен A, но более важно, объявление template <class T> void f2(T); должно быть внутри A, в противном случае функция f2<>(int) не будет другом класс A::X::Y.

Ответ 1

Мы надеемся, что более короткий ответ, чем полный от Влада:

Сущность может быть объявлена ​​несколько раз, ваша предпосылка неверна. В первом предложении важно first, поскольку эти два являются допустимыми объявлениями для функции f в пространстве имен N:

namespace N { void f(); }
void N::f() { ... }        // A definition is *also* a declaration

На этом этапе очевидна необходимость first в первом предложении, f является членом пространства имен N (первое объявление), а не глобальным пространством имен.

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

//[1]
class Y {};                       // Some type
class X {
   X(Y);                          // allow implicit conversions, 
                                  //    for exposition purposes
   friend X operator+(X, X) {...} // *first* declaration of this operator
};                                //    and also the definition
void f() {
   Y a, b;
   a + b;                         // error, no operator+ takes two Y
   X c; 
   c + b;                         // OK, ADL can find it
}

Если объявление друга не было первым объявлением, то есть если [1] заменено предыдущим объявлением:

class X;
X operator+(X,X);

При остальном остальном код будет компилироваться и вызывать operator+(X,X) преобразование a и b в X.

Последний вопрос, который у вас был, был на Assume f и g не был определен, и я считаю, что он должен читать объявленный, а не определенный. Важность этого утверждения заключается в том, что если функция была объявлена ​​перед началом, комментарий // A::f, A::g and A::h are not visible here становится ложным, поскольку предыдущее объявление делает эти функции видимыми.

Ответ 2

Вы ошибаетесь. У вас может быть несколько деклараций одной и той же функции в декларативном регионе. Например

namespace N
{
   void f( int[10] );
   void f( int[10] );
   void f( int[] );
   void f( int[] );
   void f( int * );
   void f( int * );
}

Все эти объявления объявляют ту же (и одну) функцию f.

В цитируемой цитате слово first означает, что функция друга была впервые объявлена ​​внутри определения класса. Объявление декларации внутри определения класса не существует.

Что касается комментария

//Предположим, что f и g еще не определены

то это означает, что функции еще не были объявлены. Это их первые объявления находятся внутри определения класса.

Эта часть цитаты

Каждое имя, впервые объявленное в пространстве имен, является членом этого Пространство имен. Если декларация друга в нелокальном классе сначала объявляется класс или функция, класс или функция друга является членом внутреннее пространство имен

очень ясно, объявление функции как функции друга класса внутри определения класса означает ее объявление внутри окружающего пространства имен. Однако функция не отображается, пока она не будет объявлена ​​также вне определения класса.

Чтобы продемонстрировать идею, рассмотрим следующий пример.

#include <iostream>

struct A {
    friend void f();
};
void g() { f(); }
void f() { std::cout << "It is me!" << std::endl; }

int main() {
    return 0;
}

Для этого кода компилятор выдает ошибку

prog.cpp: В функции 'void g(): prog.cpp: 9: 14: error:' f не был объявленный в этой области void g() {f(); }

Хотя функция f была объявлена ​​внутри определения класса A. И если нет никакого объявления функции перед определением класса, то функция рассматривается как член того же пространства имен, где определен класс A (точнее было бы лучше сказать, где объявлен класс, потому что класс может быть определен в некотором охватывающем пространстве имен).

Однако, если вы добавите еще одно объявление f, тогда код будет скомпилирован.

#include <iostream>

struct A {
    friend void f();
};

void f();
void g() { f(); }
void f() { std::cout << "It is me!" << std::endl; }

int main() {
    return 0;
}

И рассмотрим третий пример

#include <iostream>

void f();
namespace N {
   struct A
   {
      friend void f();
   };
   void g() { f(); }
}
void f() { std::cout << "It is me!" << std::endl; }
int main() {
    return 0;
}

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

И, наконец, рассмотрим более интересный пример

#include <iostream>

namespace N {
   class A;
}
class N::A {
    friend void f();
    int x = 10;
};

namespace N {
   //void f(); // if to uncomment the line the code will be compiled 
               // otherwise f will not be found
   void g() { f(); }
   void f() { A a; std::cout << "a.x = " << a.x << std::endl; }
}
int main() {
    return 0;
}

Здесь, хотя определение класса A находится в глобальном пространстве имен, тем не менее функция f объявляется в пространстве имен N, где и был объявлен класс.

Ответ 3

Прости меня за то, что я пропустил примеры, но хочу сохранить это короткое и сладкое.

Цель первого слова заключается в том, чтобы подчеркнуть, что объявление friend или namespace-scope является единственным способом, объявленным функцией в этой точке. Этот пункт намерен обсуждать объявления, которые создают сущность, а не более поздние сопоставления деклараций.

Возможно, изначально было бы лучше, чем раньше.

Комментарий ::f2<>(int) is a friend неверен. Не только определение функции шаблона f2(T) в пространстве имен A, но более важно, объявление template <class T> void f2(T); должно быть внутри A, в противном случае функция f2<>(int) не будет другом класса A::X::Y.

::f2 - это другое дело, чем A::f2. Цель этого примера - подчеркнуть это. Поскольку существует шаблон ::f2, а не в A, который соответствует объявлению friend void f2<>(int), этот шаблон подружился. Только потому, что нет видимой функции f, соответствующей объявлению friend void f(X);, по умолчанию она создает новую функцию в namespace A.

Однако правило довольно сложное. Последнее предложение перед запиской означает, что функция ::f все равно не будет соответствовать friend void f(X);, потому что язык предпочитает добавлять что-то к охватывающему пространству имен и рассматривает только внешние пространства имен в качестве последнего средства. Итак, это происходит следующим образом:

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