Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ADL and friend injection

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?

like image 699
uj2 Avatar asked Sep 02 '10 22:09

uj2


1 Answers

The Standard says at 14.7.1/4

A class template specialization is implicitly instantiated if the class type is used in a context that requires a completely-defined object type or if the completeness of the class type affects the semantics of the program; in particular, if an expression whose type is a class template specialization is involved in overload resolution, pointer conversion, pointer to member conversion, the class template specialization is implicitly instantiated (3.2);

Note that Vandervoorde made an issue report here, and the committee found

The standard already specifies that this creates a point of instantiation.

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).

  • If T is a template-id, its associated namespaces and classes are the namespace in which the template is defined; [... lots of noise ...]

  • If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces in which its associated classes are defined.

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 are

  • Any namespace-scope friend functions declared in associated classes are visible within their respective namespaces even if they are not visible during an ordinary lookup

There is no friend function declared in X<0> so the friend function declaration is not visible when looking into the global namespace. Note that X<0> is an entirely different class-type than X<1>. The implicit instantiation of X<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 class X<1>.

like image 87
Johannes Schaub - litb Avatar answered Sep 22 '22 21:09

Johannes Schaub - litb