Consider this code:
template <int N>
struct X
{
friend void f(X *) {}
};
int main()
{
f((X<0> *)0); // Error?
}
compilers seem to heavily disagree. (MSVC08/10 says no, GCC<4.5 says yes, but 4.5 says no, sun 5.1 says yes, intel 11.1 says yes too but comeau says no (both are EDG)).
According to “C++ Templates – The complete guide”:
… it is assumed that a call
involving a lookup for friends in
associated classes actually causes the
class to be instantiated … Although
this was clearly intended by those who
wrote the C++ standard, it is not
clearly spelled out in the standard.
I couldn’t find the relevant section in the standard. Any reference?
Consider this variation:
template <int N>
struct X
{
template <int M>
friend void f(X<M> *) {}
};
template <>
struct X<0>
{
};
int main()
{
X<1>();
f((X<0> *)0); // Error?
}
The key issue here is wether the viable function injected by X<1> should be visible during ADL for X<0>? Are they associated? All compilers mentioned above accept this code, except for Comeau which only accepts it in relaxed mode. Not sure what the standard has to say about this either.
What’s your take on that?
The Standard says at
14.7.1/4Note that Vandervoorde made an issue report here, and the committee found
For your second case – you need to consider the associated classes and namespaces of the argument
f(X<0>*). These are, since this is a pointer to a class template specialization (note that “template-id” below is not quite correct – C++0x corrected that to use the correct term) and also a pointer to a class (this confusing split was also corrected in C++0x – it lists these two cases in one bullet point).So to summary, we have as associated classes are
X<0>and the associated namespaces are the global namespace. Now the friend functions that are visible areThere is no friend function declared in
X<0>so the friend function declaration is not visible when looking into the global namespace. Note thatX<0>is an entirely different class-type thanX<1>. The implicit instantiation ofX<1>you do there has no effect on this call – it just adds a non-visible name into the global namespace that refers to a friend function of classX<1>.