Igor G
Igor G

Reputation: 2471

Can a name introduced via using-directive or using-declaration be used as elaborated-type-specifier of a friend declaration?

Why do gcc, clang and msvc successfully compile the following code? (godbolt)

namespace impl {
class A;
} // namespace impl

namespace api {
using namespace impl;
} // namespace api

class B
{
    friend class api::A;     // (1)
};

The way I see it, this particular kind of friend declaration requires elaborated-type-specifier. Elaborated-type-specifier is class keyword followed by class name. But there's no class with the name api::A in this translation unit. Using-directive doesn't declare such class, it only affects name lookup rules. So I would expect that compilation of line marked (1) fails with error that there's no class A in namespace api. But major compilers accept it... What am I missing?

And the followup question: if I replaced using-directive using namespace impl; with using-declaration using impl::A;, that would really declare name A in namespace api, thus making the code well-formed?

Upvotes: 1

Views: 90

Answers (2)

user17732522
user17732522

Reputation: 76889

First of, class api::A in your friend declaration is an elaborated-type-specifier.

But regardless, even if you drop class so that it isn't an elaborated-type-specifier anymore, lookup of names in a friend declaration is done just like any other lookup. Lookup of names (whether qualified or unqualified) in an elaborated-type-specifier is also done as usual. (There are specific rules in each case, but none which matter here.)

Only if lookup for an unqualified name in an elaborated-type-specifier doesn't find anything or if the whole declaration has certain specific forms in which the elaborated-name-specifier is the whole declaration, then it declares a class/enum with the name in some target scope.

Lookup for api::A from the class scope finds ::impl::A and so the friend declaration refers to that class (whether or not the class keyword is used).

Upvotes: 1

Serpenseth
Serpenseth

Reputation: 36

In order to understand what is happening, we must look at each element of your code:

namespace impl {
    class A;
};

is a namespace that declares class A.

namespace api {
    using namespace impl;
};

is a namespace that uses namespace impl, which has class A declared inside of it.

class B
{
    friend class api::A;     // (1)
};

is a class that has a declaration, that tells the compiler, that class A, which is in namespace api, which uses namespace impl, is a friend of class B.

It's essentially a chain. B::api -> api::impl -> impl::A.

Upvotes: -1

Related Questions