объявление класса друга и использование директивы

Правильно ли построен следующий пример?

namespace N {
    class A;
}
using namespace N;

class B {
    int i;
    friend class A;
};

namespace N {
    class A {
        B m;
        int get() { return m.i; }
    };
}

Этот пример успешно скомпилирован с Clang 3.5, но не удалось с g++ 4.8.1 со следующим:

main.cpp: In member function ‘int N::A::get()’:
main.cpp:7:9: error: ‘int B::i’ is private
     int i;
         ^
main.cpp:14:30: error: within this context
         int get() { return m.i; }
                              ^

Стандарт С++ 11 §7.3.1.2 p3 говорит,

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

Например, class A не является членом самого внутреннего окружающего пространства имен (т. е. глобального пространства имен), но class A вводится с помощью директивы using в глобальное пространство имен.


person Mitsuru Kariya    schedule 09.01.2014    source источник
comment
Вы создаете вложенное пространство имен N внутри N из-за using namespace N, попробуйте удалить последний namespace N   -  person JuanR    schedule 09.01.2014
comment
@ichramm: Эм, правда?   -  person Lightness Races in Orbit    schedule 09.01.2014
comment
@ichramm using namespace N не означает, что все последующее неявно принадлежит namespace N   -  person Praetorian    schedule 09.01.2014
comment
@LightnessRacesinOrbit @Praetorian Я не был уверен, пока не протестировал приведенный выше код, комментирующий последнее «пространство имен N», это странно, я знаю.   -  person JuanR    schedule 09.01.2014
comment
@ichramm: Это не просто странно: это неправда. Вы наблюдаете что-то другое.   -  person Lightness Races in Orbit    schedule 09.01.2014
comment
@LightnessRacesinOrbit Думаю, ты был на правильном пути. Код действителен, но не N::A получает преимущества друга. Это еще не существующий ::A. Вот почему он не компилируется с get(). (ИМО....)   -  person jrok    schedule 09.01.2014
comment
@ichramm Ваш эксперимент достоин восхищения, а ваш вывод - меньше.   -  person Alan Stokes    schedule 09.01.2014
comment
@jrok: Я думаю, что это был и мой вывод. Но мне было скучно качели :(   -  person Lightness Races in Orbit    schedule 09.01.2014
comment
Спасибо за разъяснение, еще одна интересная мысль заключается в том, что изменение заявления о друзьях с помощью friend class N::A, похоже, также решает проблему.   -  person JuanR    schedule 09.01.2014
comment
Интересное замечание: using N::A; вместо using namespace N; прекрасно компилируется. Я готов классифицировать это как ошибку.   -  person Rapptz    schedule 09.01.2014
comment
И обычный friend A; тоже компилируется.   -  person TemplateRex    schedule 09.01.2014
comment
[namespace.udir]/2 Во время поиска неполного имени имена отображаются так, как если бы они были объявлены в ближайшем окружающем пространстве имен, которое содержит как using-directive, так и назначенное пространство имен. И [basic.lookup.elab]/2 Если уточненный-спецификатор-типа не имеет описатель-вложенного-имени, и если определенный-спецификатор-типа появляется в объявлении в следующей форме: ключ класса атрибута-спецификатор-seq opt идентификатор ; идентификатор просматривается в соответствии с 3.4.1 [поиск неквалифицированного имени], но игнорируются все неполные -типы имен, которые были объявлены.   -  person dyp    schedule 10.01.2014
comment
Я думаю, что в процитированном §7.3.1.2 p3 только говорится, что пространства имен, включающие самое внутреннее, не ищутся. Но директива using позволяет поиску имен находить имена, как если бы они были объявлены в самом внутреннем охватывающем пространстве имен (здесь).   -  person dyp    schedule 10.01.2014


Ответы (2)


В то время как использование пространства имен N извлекает имя N::A в глобальное пространство имен, оно не объявляет это A в глобальном пространстве имен. Следовательно, дополнительный A в глобальном пространстве имен является другом B. clang неверен.

person Community    schedule 09.01.2014
comment
Стандарт @Diether С++ 11 7.3.4 p2 говорит, что во время поиска неполного имени (3.4.1) имена отображаются как если бы они были объявлены ..., а 3.4.1 p2 говорит: «Для этой цели». из правил поиска неполных имен, описанных в 3.4.1, объявления из пространства имен, назначенного директивой-использования, считаются членами этого объемлющего пространства имен. - person Mitsuru Kariya; 10.01.2014
comment
@Дитер: Спасибо! Вы очень правы! Я обнаружил проблему N1229. Однако я думаю, что стандарт C++11 должен показать эту ситуацию более четко. - person Mitsuru Kariya; 10.01.2014

Чтобы сделать N::A без уточнения friend из B, вы должны использовать

friend A;

скорее, чем

friend class A;

При использовании уточненного спецификатора типа, т. е. class A, в этой конкретной форме он вводит имя класса (см. 3.4.4 [basic.lookup.elab], параграф 2).

person Dietmar Kühl    schedule 09.01.2014
comment
Насколько я вижу, форма, упомянутая в [basic.lookup.elab]/2, не содержит friend. Таким образом, упомянутая там форма является частью объявления о дружбе. Это все еще применимо? - person dyp; 10.01.2014
comment
@dyp: да, я в курсе. Однако friend class A; соответствует выражению .* class-key attribute-specifier-seq? идентификатор;. Я думаю, что цель состоит в том, чтобы отличить его от таких вариантов использования, как class foo f(); или int stat(struct stat*) и т. д., и я думаю, что это применимо. - person Dietmar Kühl; 10.01.2014