Friends again... (no templates :)

  • Thread starter Andrey Tarasevich
  • Start date
A

Andrey Tarasevich

Hello

Consider the following code fragment

namespace N {
struct S {
void foo();
};
}

using namespace N; // 1

class C {
friend struct S; // 2
C() {}
};

void N::S::foo() {
C c; // 3 Is C's constructor accessible here???
}

According to what I can see in the standard, friend declaration at point
2 is supposed to perform name lookup for unqualified name 'S' in order
to determine whether this is the first declaration of 'S'. According to
7.3.1.2/3, the lookup goes up to the innermost enclosing namespace scope
(which happens to be the global scope in this case). According to
3.4.1/2, name 'S' introduced by a using directive at point 1 is
considered to be a member of global namespace for the purposes of
unqualified name lookup. This makes me to conclude that the friend
declaration at 2 is supposed to refer to 'N::S' instead of introducing a
new [hidden] declaration of 'struct S' into the global namespace. I.e.
the constructor of 'C' is supposed to be accessible at point 3.

Experiments show that GCC 3.3.3 accepts the code. So does MSVC++ 2005
(for what it's worth). But Comeau Online rejects it, complaining that
the constructor is inaccessible at 3. I just found out that GCC 4.1 also
refuses to compile this code for the same reason.

Did something change in the language specification recently? Something
that applies in this case?
 
G

Greg Herlihy

Consider the following code fragment

namespace N {
struct S {
void foo();
};
}

using namespace N; // 1

class C {
friend struct S; // 2
C() {}
};

void N::S::foo() {
C c; // 3 Is C's constructor accessible here???
}

According to what I can see in the standard, friend declaration at point
2 is supposed to perform name lookup for unqualified name 'S' in order
to determine whether this is the first declaration of 'S'. According to
7.3.1.2/3, the lookup goes up to the innermost enclosing namespace scope
(which happens to be the global scope in this case). According to
3.4.1/2, name 'S' introduced by a using directive at point 1 is
considered to be a member of global namespace for the purposes of
unqualified name lookup. This makes me to conclude that the friend
declaration at 2 is supposed to refer to 'N::S' instead of introducing a
new [hidden] declaration of 'struct S' into the global namespace. I.e.
the constructor of 'C' is supposed to be accessible at point 3.

A using directive like the one above ("using namespace N") does not
"introduce" any names into the declarative region in which the directive
appears. Instead, a using directive merely affects how names are looked up
within that declarative region.

Since a friend declaration requires that the named friend (S in this case)
be a member of the enclosing declarative region (if the friend is to be
found there), a using directive is not enough in this case for the compiler
to find the intended friend class: N::S.

Since a using declaration does introduce a name into its enclosing region,
it is possible to fix this problem by using a using declaration instead of
using a using directive to bring the name S into the proper declarative
region. For example:

using N::S; // 1

class C
{
friend struct S; // 2
C() {}
};

Greg
 
S

Sumit Rajan

Andrey said:
Hello

Consider the following code fragment

namespace N {
struct S {
void foo();
};
}

using namespace N; // 1

class C {
friend struct S; // 2
public:

C() {}
};

void N::S::foo() {
C c; // 3 Is C's constructor accessible here???
}

Comeau should now be able to compile it.

Regards,
Sumit.
 
A

Andrey Tarasevich

Greg said:
Consider the following code fragment

namespace N {
struct S {
void foo();
};
}

using namespace N; // 1

class C {
friend struct S; // 2
C() {}
};

void N::S::foo() {
C c; // 3 Is C's constructor accessible here???
}

According to what I can see in the standard, friend declaration at point
2 is supposed to perform name lookup for unqualified name 'S' in order
to determine whether this is the first declaration of 'S'. According to
7.3.1.2/3, the lookup goes up to the innermost enclosing namespace scope
(which happens to be the global scope in this case). According to
3.4.1/2, name 'S' introduced by a using directive at point 1 is
considered to be a member of global namespace for the purposes of
unqualified name lookup. This makes me to conclude that the friend
declaration at 2 is supposed to refer to 'N::S' instead of introducing a
new [hidden] declaration of 'struct S' into the global namespace. I.e.
the constructor of 'C' is supposed to be accessible at point 3.

A using directive like the one above ("using namespace N") does not
"introduce" any names into the declarative region in which the directive
appears.

Yes, it does "virtually" introduce the names into the region
("virtually" here means that the names are introduced for the purpose of
name lookup only). That is exactly what 3.4.1/2 says:

"For the purpose of of the unqualified name lookup rules described in
3.4.1, the declarations from the namespace nominated by the
'using-directive' are considered members of that enclosing namespace."
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Instead, a using directive merely affects how names are looked up
within that declarative region.

Well, that is exactly _how_ in affects the lookup process. By
introducing the names into the region _as_ _members_ (for the purposes
of unqualified name lookup only).
Since a friend declaration requires that the named friend (S in this case)
be a member of the enclosing declarative region (if the friend is to be
found there), a using directive is not enough in this case for the compiler
to find the intended friend class: N::S.

The standard does not agree with you on that. Once again, it literally
says that in the above example for the purposes of unqualified name
lookup name 'S' (standing for 'N::S') is considered a member of global
namespace.
Since a using declaration does introduce a name into its enclosing region,
it is possible to fix this problem by using a using declaration instead of
using a using directive to bring the name S into the proper declarative
region. For example:

using N::S; // 1

class C
{
friend struct S; // 2
C() {}
};

This will cause at least Comeau Online to issue a different error - at
point 2 it will say that name 'S' has already been declared in this
scope. It is the same problem as in

namespace N { struct S {}; }

using N::S;
struct S {}; // ERROR: 'S' has already been declared

Apparently, in your modified example Comeau still refuses to link friend
declaration to 'N::S'. I might look into that separately later, but at
this time I'm primarily interested in the original version with using
directive.
 
A

Andrey Tarasevich

Andrey said:
...
Did something change in the language specification recently? Something
that applies in this case?
...

OK, it looks like it all comes from here

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#138
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2000/n1229.html

It all still appears to be in the drafting stage, but according to the
recommendations section in 'n1229' using directives should not be
considered by name lookup performed for friend declarations. In other
words, Comeau Online and GCC 4.1 do indeed violate the requirements of
the current language specification by implementing on of the possible
future changes in it (a bit prematurely, I'd say).
 
A

Andrey Tarasevich

Andrey said:

It looks like the situation with correct support of "friends" is pretty
grim even when it comes to simpler cases. In the following code

struct B { void foo(); };

namespace X {
class A {
A() {}
friend struct B; // refers to X::B, not to ::B
};

struct B {
void foo();
};
}

void X::B::foo() {
X::A a; // OK
}

void B::foo() {
X::A a; // ERROR
}

compilers are supposed to accept the 'OK' line and reject the 'ERROR'
one. In practice so far I only found that Comeau Online and Forte
Developer 7 C++ 5.4 behave properly. MS C++ compilers (including 2005),
GCC 3 get it wrong. And that despite the fact that the standard even has
a specific example dedicated to this situation in 7.3.1.2/3...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top