Подтвердить что ты не робот

Какова область действия встроенного друга?

После поиска aroung SO один вопрос научил меня, что лексическая область функции встроенного друга - это класс, который он определил, то есть он может получить доступ, например. typedef в классе без их квалификации. Но потом я подумал, что каков фактический объем такой функции? GCC по крайней мере отвергает все мои попытки вызвать его. Может ли функция, например, в этом случае когда-либо вызываться через другие средства, отличные от ADL, что невозможно здесь без аргументов?

Стандартные цитаты оценены, так как я в настоящее время не могу получить доступ к моей копии.

Следующий код

namespace foo{
  struct bar{
    friend void baz(){}
    void call_friend();
  };
}

int main(){
  foo::baz();           // can't access through enclosing scope of the class
  foo::bar::baz();    // can't access through class scope
}

namespace foo{
  void bar::call_friend(){
    baz();    // can't access through member function
  }
}

приводит к следующим ошибкам:

prog.cpp: In function ‘int main()’:
prog.cpp:9: error: ‘baz’ is not a member of ‘foo’
prog.cpp:10: error: ‘baz’ is not a member of ‘foo::bar’
prog.cpp: In member function ‘void foo::bar::call_friend()’:
prog.cpp:15: error: ‘baz’ was not declared in this scope
4b9b3361

Ответ 1

Когда вы объявляете функцию friend с неквалифицированным идентификатором в классе, она называет функцию в ближайшем окружении пространства имен.

Если эта функция ранее не была объявлена, объявление friend не делает эту функцию видимой в этой области для обычного поиска. Это делает объявленную функцию видимой для зависимого от аргумента поиска.

Это подчеркивается во многих примечаниях, но окончательное утверждение содержится в 7.3.1.2/3 (ISO/IEC 14882: 2011):

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

Ответ 2

"Язык программирования С++ 3rd Edition (Stroustrap)": p279:

я. "Как объявление участника, декларация друга не вводит имя в охватывающую область"

II. "Класс друга должен быть предварительно объявлен в закрывающей области или определен в неклассе scope, который сразу же включает класс, объявляющий его другом "

III. "Функция друга может быть явно объявлена ​​так же, как и классы друзей, или ее можно найти через ее типы аргументов (§8.2.6), как если бы она была объявлена ​​в неклассе область действия, сразу же включающая его класс. "

IV. "Из этого следует, что функция друга должна быть явно объявлена ​​в охватывающей области или принимать аргумент своего класса. Если нет, то друг не может быть вызван. Например:"

//no f() here
void g();
class X{
    friend void f();          //useless
    friend void g();          //can be found because it is declared outside of class scope
    friend void h(const X&);  //can be found because the arguments access class members
};

void f() { }                 //enemy of X :)

Но в вашем случае есть еще что-то, что связано с пространством имен, потому что если вы поместите правильное объявление в foo, например:

namespace foo{
  struct bar{
    friend void baz(const &bar){};
    void call_friend();
  }
}

не компилируется. Однако, если вы объявляете его вне foo, это работает как шарм. Теперь рассмотрим, что на самом деле глобальные, локальные, структурные и классы на самом деле являются пространствами имен. Теперь это приводит к выводу, что baz(const &) неявно определяется в глобальной области.

Это компилируется:

namespace foo{
  struct bar{
    friend void baz(const bar&){};
    void call_friend();
  };
}

int main(){
    foo::bar k;
    baz(k);
    return 0;
}

Следовательно, есть две проблемы:

  • В объявлении друга не вводится имя в охватывающей области, кроме IV. Таким образом, оригинальная программа не может найти baz(), потому что она не была объявлена ​​должным образом.
  • Если IV, то есть ADL, то функция найдена в foo, но не может быть доступна как foo:: baz (k) из-за ADL. Вам нужно будет явно определить baz (const bar &) в foo для доступа к нему по квалифицированному имени.

Спасибо, надеюсь, что это поможет, но, конечно, мне понравилась задача:).

Ответ 3

Интересно!

Похоже, что компилятор не знает, к какой области он относится (и, честно говоря, нет никаких подсказок) и, таким образом, не имеет границ. Я полагаю, что некоторые стандартные копания появляются.

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

namespace foo
{
  void baz();   // declare it here and now it works in foo namespace etc.
  struct bar
  {
    friend void baz(){}
    void call_friend();
  };
}

Выкапывая стандарт, я нахожу:

11.3 Друзья [class.friend]

Пункт 6

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

[ Example:
class M { friend void f() { }       // definition of global f, a friend of M,
                                    // not the definition of a member function
};
— end example ]

Пункт 7

Такая функция неявно встроена. Функция друга, определенная в классе , находится в (лексическом) объеме класса, в котором она определена. Функция друга, определенная вне класса, не является (3.4.1).

Примечание:

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

Ответ 4

В этом примере

namespace foo{
  struct bar{
    friend void baz(){}
    void call_friend();
  };
}

int main(){
  foo::baz();           // can't access through enclosing scope of the class
  foo::bar::baz();    // can't access through class scope
}

namespace foo{
  void bar::call_friend(){
    baz();    // can't access through member function
  }
}
  • foo::baz() недоступен, поскольку имя baz не отображается в пространстве имен foo. Если я правильно помню (§ 3.4/2), то здесь.

  • foo::bar::baz() недоступен, потому что друзья не являются членами класса, а область действия встроенного друга - это пространство имен или класс, в которых существует их определение, поэтому вы не можете получить к ним доступ за пределами этой области.

Если вы поместите объявление baz() в foo, имя функции baz будет отображаться в foo, и определение будет просмотрено во вложенной области bar.

namespace foo{
  void baz();  // declaration at namespace scope
  struct bar{
    friend void baz(){}
  };

  void call_friend() {
     baz();  // ok
  }
}

int main()
{
    foo::baz(); // ok, bar will be looked up in the nested scope of foo::bar.
}

Ответ 5

Я думаю, вы путаете friend и private. Объявив функцию a friend, вы предоставляете ей доступ к своим частным членам и не разрешаете другим функциям доступ к ней. В любом случае любая функция-член из struct доступна любому объекту, потому что члены struct являются общедоступными по умолчанию.

Однако в вашем случае baz недоступен, потому что, выполнив friend void baz(){}, вы действительно не объявили функцию baz, вы просто сказали, что это функция friend. Вы можете просто удалить ключевое слово friend, и оно решит все проблемы.