Каков объем встроенных функций друзей? - PullRequest
32 голосов
/ 21 ноября 2011

После поиска по 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

Ответы [ 5 ]

35 голосов
/ 27 ноября 2011

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

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

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

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

4 голосов
/ 29 ноября 2011

«Язык программирования C ++, 3-е издание (Stroustrap)»: p279:

I. «Как и декларация члена, декларация друга не вводит имя в ограничивающую область»

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

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, например ::1122*

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;
}

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

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

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

2 голосов
/ 21 ноября 2011

Интересно!

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

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

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).

Примечание:

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

0 голосов
/ 21 ноября 2011

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

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
  }
}
  1. foo::baz() недоступен, поскольку имя baz не отображается в области пространства имен foo.Если я правильно помню (§ 3.4 / 2) применяется здесь.

  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.
}
0 голосов
/ 21 ноября 2011

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

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

...