Reputation: 45434
I have code with the following basic structure
namespace my {
template<typename> class A; // forward declaration
namespace details {
template<typename T> class B
{
const T*const t;
B(const T*x) : t(x) {} // non-public constructor
template<typename> friend class A; // friend declaration
};
}
template<typename T> class A
{
T*const t;
public:
A(T*x) : t(x) {}
details::B<T> makeb() const // offending method:
{ return details::B<T>(t); } // calls non-public constructor
};
}
which compiled fine under gcc (4.7 & 4.8, using -std=c++11
) and icpc (13.1), but not clang (using -std=c++11 -stdlib=libc++
), which complains about usage of a non-public constructor (when instantinating the offending method). It turns out that clang is happy if the friend declaration gives the full qualified name, as in
template<typename> friend class my::A;
Is this a bug of clang? I would have thought that any visible symbol in the enclosing namespace my
is available without further qualification in the inner namespace my::details
. What does the 2011 standard say?
Upvotes: 3
Views: 1911
Reputation: 56863
I think Clang is right:
7.3.1.2 Namespace member definitions [namespace.memdef]
3 Every name first declared in a namespace is a member of that namespace. If a
friend
declaration in a non-local class first declares a class, function, class template or function template the friend is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function or function template is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). If the name in afriend
declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace. [ Note: The other forms of friend declarations cannot declare a new member of the innermost enclosing namespace and thus follow the usual lookup rules. — end note ] [ Example:
// Assume f and g have not yet been declared.
void h(int);
template <class T> void f2(T);
namespace A {
class X {
friend void f(X); // A::f(X) is a friend
class Y {
friend void g(); // A::g is a friend
friend void h(int); // A::h is a friend
// ::h not considered
friend void f2<>(int); // ::f2<>(int) is a friend
};
};
// A::f, A::g and A::h are not visible here
X x;
void g() { f(x); } // definition of A::g
void f(X) { /* ... */} // definition of A::f
void h(int) { /* ... */ } // definition of A::h
// A::f, A::g and A::h are visible here and known to be friends
}
using A::x;
void h() {
A::f(x);
A::X::f(x); // error: f is not a member of A::X
A::X::Y::g(); // error: g is not a member of A::X::Y
}
— end example ]
Note the // ::h not considered
.
Upvotes: 4