Namespace causes sub-optimal template overload resolution - c++

This is very similar to this question, but I'm not sure the answer there is entirely applicable to the minimal code I've put together that demonstrates the issue. (My code does not use trailing-return types, and there are some other differences as well.) Additionally, the issue of whether MSVC's behavior is legal doesn't seem to be addressed.
In short, I'm seeing the compiler select a generic function template instantiation rather than a more-specific overload when the function template is inside a namespace.
Consider the following set of namespace and class definitions:
namespace DoStuffUtilNamespace
{
template<typename UNKNOWN>
void doStuff(UNKNOWN& foo)
{
static_assert(sizeof(UNKNOWN) == -1, "CANNOT USE DEFAULT INSTANTIATION!");
}
}
class UtilForDoingStuff
{
public:
template <typename UNKNOWN>
void doStuffWithObjectRef(UNKNOWN& ref)
{
DoStuffUtilNamespace::doStuff(ref);
}
};
class MyClassThatCanDoStuff { };
namespace DoStuffUtilNamespace
{
using ::MyClassThatCanDoStuff; // No effect.
void doStuff(MyClassThatCanDoStuff& foo) { /* No assertion! */ }
}
... and the following use-cases:
int main()
{
MyClassThatCanDoStuff foo;
DoStuffUtilNamespace::MyClassThatCanDoStuff scoped_foo;
UtilForDoingStuff util;
DoStuffUtilNamespace::doStuff(foo); // Compiles
DoStuffUtilNamespace::doStuff(scoped_foo); // Compiles
util.doStuffWithObjectRef(foo); // Triggers static assert
util.doStuffWithObjectRef(scoped_foo); // Triggers static assert
}
If the entire DoStuffUtilNamespace is eliminated and all its members are moved to global scope, this compiles fine with G++ and Clang++.
With the namespace, doStuff is of course a dependent name. According to the top-voted answer on the similar question, the standard says:
In resolving dependent names, names from the following sources are considered:
Declarations that are visible at the point of definition of the template.
Declarations from namespaces associated with the types of the function arguments both from the instantiation context and from the definition context.
This seems a little odd to me; I don't understand why the first bullet point would specify that the declarations must be visible at the point of definition of the template rather than at the point of instantiation, since the second bullet point explicitly specifies that some declarations visible only at the point of instantiation are allowed. (If someone would like to offer a rationale, I'd appreciate it, but that's not my question because it's my understanding that questions of the form "why did the standards committee decide X" are off topic.)
So I think that explains why util.doStuffWithObjectRef(foo); triggers the static assertion: doStuff(MyClassThatCanDoStuff&) hasn't been declared at the point of definition of UtilForDoingStuff::doStuffWithObjectRef<UNKNOWN>(UNKNOWN&). And indeed moving the class UtilForDoingStuff definition after the doStuff overload has been defined seems to fix the issue.
But what exactly does the standard mean by "namespaces associated with the types of the function arguments"? Shouldn't the using ::MyClassThatCanDoStuff declaration, together with the explicit scoping of the scoped_foo instance type within the namespace, trigger argument-dependent lookup, and shouldn't this look-up find the non-asserting definition of doStuff()?
Also, the entire code is compiled without error using clang++ -ftemplate-delayed-parsing, which emulates MSVC's template-parsing behavior. This seems preferable, at least in this particular case, because the ability to add new declarations to a namespace at any time is one of the primary appeals of namespaces. But, as noted above, it doesn't quite seem to follow the letter of the law, according to the standard. Is it permissible, or is it an instance of non-conformance?
EDIT:: As pointed out by KIIV, there is a workaround; the code compiles if template specialization is used instead of overloading. I would still like to know the answers to my questions about the standard.

With the namespace, doStuff is of course a dependent name.
You are starting from the wrong premise. There is no ADL for a qualified call like DoStuffUtilNamespace::doStuff(ref). [basic.lookup.argdep]/p1, emphasis mine:
When the postfix-expression in a function call (5.2.2) is an
unqualified-id, other namespaces not considered during the usual
unqualified lookup (3.4.1) may be searched, and in those namespaces,
namespace-scope friend function or function template declarations
(11.3) not otherwise visible may be found.
DoStuffUtilNamespace::doStuff is a qualified-id, not an unqualified-id. ADL doesn't apply.
For this reason, DoStuffUtilNamespace::doStuff is also not a dependent name. [temp.dep]/p1:
In an expression of the form:
postfix-expression ( expression-listopt)
where the postfix-expression is an unqualified-id, the
unqualified-id denotes a dependent name if [...]. If an operand of an operator is a type-dependent expression, the operator also denotes
a dependent name. Such names are unbound and are looked up at the
point of the template instantiation (14.6.4.1) in both the context of
the template definition and the context of the point of instantiation
(The italicization of dependent name indicate that this paragraph is defining the term.)
Instead, per [temp.nondep]/p1:
Non-dependent names used in a template definition are found using the
usual name lookup and bound at the point they are used.
which doesn't find your later overload declaration.
Specialization works because it's still the same function template declaration that's used; you just supplied a different implementation than the default one.
But what exactly does the standard mean by "namespaces associated with
the types of the function arguments"? Shouldn't the using ::MyClassThatCanDoStuff declaration, together
with the explicit scoping of the scoped_foo instance type within the
namespace, trigger argument-dependent lookup
No. using-declarations do not affect ADL. [basic.lookup.argdep]/p2, emphasis mine:
For each argument type T in the function call, there is a set of
zero or more associated namespaces and a set of zero or more
associated classes to be considered. The sets of namespaces and
classes is determined entirely by the types of the function arguments
(and the namespace of any template template argument).
Typedef names and using-declarations used to specify the types do not contribute to this set. The sets of namespaces and classes are
determined in the following way:
If T is a fundamental type, [...]
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
innermost enclosing namespaces of its associated classes. Furthermore,
if T is a class template specialization, its associated namespaces and
classes also include: the namespaces and classes associated with the
types of the template arguments provided for template type parameters
(excluding template template parameters); the namespaces of which any
template template arguments are members; and the classes of which any
member templates used as template template arguments are members. [
Note: Non-type template arguments do not contribute to the set of associated namespaces. —end note ]
[...]

With template specialization I can get it work:
namespace DoStuffUtilNamespace
{
template<typename UNKNOWN>
void doStuff(UNKNOWN& foo)
{
static_assert(sizeof(UNKNOWN) == -1, "CANNOT USE DEFAULT INSTANTIATION!");
}
}
class UtilForDoingStuff
{
public:
template <typename UNKNOWN>
void doStuffWithObjectRef(UNKNOWN& ref)
{
DoStuffUtilNamespace::doStuff(ref);
}
};
class MyClassThatCanDoStuff { };
namespace DoStuffUtilNamespace
{
using ::MyClassThatCanDoStuff;
template <> void doStuff<MyClassThatCanDoStuff>(MyClassThatCanDoStuff& foo) { /* No assertion! */ }
}
int main()
{
MyClassThatCanDoStuff foo;
DoStuffUtilNamespace::MyClassThatCanDoStuff scoped_foo;
UtilForDoingStuff util;
DoStuffUtilNamespace::doStuff(foo); // Compiles
DoStuffUtilNamespace::doStuff(scoped_foo); // Compiles
util.doStuffWithObjectRef(foo); // Compiles
util.doStuffWithObjectRef(scoped_foo); // Compiles
}

Declarations from namespaces associated with the types of the function arguments both from the instantiation context and from the definition context.
Example with the following code which prints B::foo Demo
namespace A
{
template <typename T>
void foo(const T&) {std::cout << "A::foo" << std::endl;}
template <typename T>
void bar(const T& t) {
foo(t); // thank to ADL, it will also look at B::foo for B::S.
}
}
namespace B
{
struct S {};
void foo(const S&) {std::cout << "B::foo" << std::endl;}
}
int main()
{
B::S s;
A::bar(s);
}
So when calling ?::foo(const B::S&), the second bullet point adds B::foo to the list of overloads.
why template-specialization works in this case
There is only one function:
template<>
void DoStuffUtilNamespace::doStuff<MyClassThatCanDoStuff>(MyClassThatCanDoStuff& foo);
even if it is defined later.
Note that the fact that there is a specialization should be known in the translation unit, else the program is ill formed (doesn't respect ODR).
while overloading doesn't.
You think:
So I think that explains why util.doStuffWithObjectRef(foo); triggers the static assertion: doStuff(MyClassThatCanDoStuff&) hasn't been declared at the point of definition of UtilForDoingStuff::doStuffWithObjectRef<UNKNOWN>(UNKNOWN&). And indeed moving the class UtilForDoingStuff definition after the doStuff overload has been defined seems to fix the issue.
Exactly.

Related

Question about resolution of free function names in C++ templates

This program works as expected:
#include <iostream>
template <typename T>
void output(T t) {
prt(t);
}
struct It {
It(int* p) : p(p) {}
int* p;
};
void prt(It it) {
std::cout << *(it.p) << std::endl;
}
int main() {
int val = 12;
It it(&val);
output(it);
return 0;
}
When you compile and execute this, it prints "12" as it should. Even though the function prt, required by the output template function, is defined after output, prt is visible at the point of instantiation, and therefore everything works.
The program below is very similar to the program above, but it fails to compile:
#include <iostream>
template <typename T>
void output(T t) {
prt(t);
}
void prt(int* p) {
std::cout << (*p) << std::endl;
}
int main() {
int val = 12;
output(&val);
return 0;
}
This code is trying to do the same thing as the previous example, but this fails in gcc 8.2 with the error message:
'prt' was not declared in this scope, and no declarations were found by
argument-dependent lookup at the point of instantiation [-fpermissive]
The only thing that changed is that the argument passed to output is a built-in type, rather than a user-defined type. But I didn't think that should matter for name resolution. So my question is: 1) why does the second example fail?; and 2) why does one example fail and the other succeeds?
The Standard rule that applies here is found in [temp.dep.candidate]:
For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules ([basic.lookup.unqual], [basic.lookup.argdep]) except that:
For the part of the lookup using unqualified name lookup, only function declarations from the template definition context are found.
For the part of the lookup using associated namespaces ([basic.lookup.argdep]), only function declarations found in either the template definition context or the template instantiation context are found.
In both examples, unqualified name lookup finds no declarations of prt, since there were no such declarations before the point where the template was defined. So we move on to argument-dependent lookup, which looks only in the associated namespaces of the argument types.
Class It is a member of the global namespace, so the global namespace is the one associated namespace, and the one declaration is visible within that namespace in the template instantiation context.
A pointer type U* has the same associated namespaces as type U, and a fundamental type has no associated namespaces at all. So since the only argument type int* is a pointer to fundamental type, there are no associated namespaces, and argument-dependent lookup can't possibly find any declarations in the second program.
I can't exactly say why the rules were designed this way, but I would guess the intent is that a template should either use the specific declared functions it meant to use, or else use a function as an extensible customization point, but those user customizations need to be closely related to a user-defined type they will work with. Otherwise, it becomes possible to change the behavior of a template that really meant to use one specific function or function template declaration by providing a better overload for some particular case. Admittedly, this is more from the viewpoint of when there is at least one declaration in the template definition context, not when that lookup finds nothing at all, but then we get into cases where SFINAE was counting on not finding something, etc.

Why doesn't function declared inside other function participate in argument dependent lookup?

Consider a simple example:
template <class T>
struct tag { };
int main() {
auto foo = [](auto x) -> decltype(bar(x)) { return {}; };
tag<int> bar(tag<int>);
bar(tag<int>{}); // <- compiles OK
foo(tag<int>{}); // 'bar' was not declared in this scope ?!
}
tag<int> bar(tag<int>) { return {}; }
Both [gcc] and [clang] refuses to compile the code. Is this code ill-formed in some way?
foo(tag<int>{}); triggers the implicit instantiation of a specialization of the function call operator member function template of foo's closure type with the template argument tag<int>. This creates a point of instantiation for this member function template specialization. According to [temp.point]/1:
For a function template specialization, a member function template
specialization, or a specialization for a member function or static
data member of a class template, if the specialization is implicitly
instantiated because it is referenced from within another template
specialization and the context from which it is referenced depends on
a template parameter, the point of instantiation of the specialization
is the point of instantiation of the enclosing specialization.
Otherwise, the point of instantiation for such a specialization
immediately follows the namespace scope declaration or definition
that refers to the specialization.
(emphasis mine)
So, the point of instantiation is immediately after main's definition, before the namespace-scope definition of bar.
Name lookup for bar used in decltype(bar(x)) proceeds according to [temp.dep.candidate]/1:
For a function call where the postfix-expression is a dependent
name, the candidate functions are found using the usual lookup rules
(6.4.1, 6.4.2) except that:
(1.1) — For the part of the lookup using unqualified name lookup
(6.4.1), only function declarations from the template definition
context are found.
(1.2) — For the part of the lookup using associated namespaces
(6.4.2), only function declarations found in either the template
definition context or the template instantiation context are found. [...]
Plain unqualified lookup in the definition context doesn't find anything. ADL in the definition context doesn't find anything either. ADL in the instantiation context, according to [temp.point]/7:
The instantiation context of an expression that depends on the
template arguments is the set of declarations with external linkage
declared prior to the point of instantiation of the template
specialization in the same translation unit.
Again, nothing, because bar hasn't been declared at namespace scope yet.
So, the compilers are correct. Moreover, note [temp.point]/8:
A specialization for a function template, a member function template,
or of a member function or static data member of a class template may
have multiple points of instantiations within a translation unit, and
in addition to the points of instantiation described above, for any
such specialization that has a point of instantiation within the
translation unit, the end of the translation unit is also considered
a point of instantiation. A specialization for a class template has
at most one point of instantiation within a translation unit. A
specialization for any template may have points of instantiation in
multiple translation units.
If two different points of instantiation give a template specialization different meanings according to the one-definition rule
(6.2), the program is ill-formed, no diagnostic required.
(emphasis mine)
and the second part of [temp.dep.candidate]/1:
[...] If the call would be ill-formed or would find a better match had
the lookup within the associated namespaces considered all the
function declarations with external linkage introduced in those
namespaces in all translation units, not just considering those
declarations found in the template definition and template
instantiation contexts, then the program has undefined behavior.
So, ill-formed NDR or undefined behavior, take your pick.
Let's consider the example from your comment above:
template <class T>
struct tag { };
auto build() {
auto foo = [](auto x) -> decltype(bar(x)) { return {}; };
return foo;
}
tag<int> bar(tag<int>) { return {}; }
int main() {
auto foo = build();
foo(tag<int>{});
}
Lookup in the definition context still doesn't find anything, but the instantiation context is immediately after main's definition, so ADL in that context finds bar in the global namespace (associated with tag<int>) and the code compiles.
Let's also consider AndyG's example from his comment above:
template <class T>
struct tag { };
//namespace{
//tag<int> bar(tag<int>) { return {}; }
//}
auto build() {
auto foo = [](auto x) -> decltype(bar(x)) { return {}; };
return foo;
}
namespace{
tag<int> bar(tag<int>) { return {}; }
}
int main() {
auto foo = build();
foo(tag<int>{});
}
Again, the instantiation point is immediately after main's definition, so why isn't bar visible? An unnamed namespace definition introduces a using-directive for that namespace in its enclosing namespace (the global namespace in this case). This would make bar visible to plain unqualified lookup, but not to ADL according to [basic.lookup.argdep]/4:
When considering an associated namespace, the lookup is the same as
the lookup performed when the associated namespace is used as a
qualifier (6.4.3.2) except that:
(4.1) — Any using-directives in the associated namespace are
ignored. [...]
Since only the ADL part of the lookup is performed in the instantiation context, bar in the unnamed namespace is not visible.
Commenting out the lower definition and uncommenting the upper one makes bar in the unnamed namespace visible to plain unqualified lookup in the definition context, so the code compiles.
Let's also consider the example from your other comment above:
template <class T>
struct tag { };
int main() {
void bar(int);
auto foo = [](auto x) -> decltype(bar(decltype(x){})) { return {}; };
tag<int> bar(tag<int>);
bar(tag<int>{});
foo(tag<int>{});
}
tag<int> bar(tag<int>) { return {}; }
This is accepted by GCC, but rejected by Clang. While I was initially quite sure that this is a bug in GCC, the answer may actually not be so clear-cut.
The block-scope declaration void bar(int); disables ADL according to [basic.lookup.argdep]/3:
Let X be the lookup set produced by unqualified lookup (6.4.1) and let
Y be the lookup set produced by argument dependent lookup (defined as
follows). If X contains
(3.1) — a declaration of a class member, or
(3.2) — a block-scope function declaration that is not a
using-declaration, or
(3.3) — a declaration that is neither a function nor a function
template
then Y is empty. [...]
(emphasis mine)
Now, the question is whether this disables ADL in both the definition and instantiation contexts, or only in the definition context.
If we consider ADL disabled in both contexts, then:
The block-scope declaration, visible to plain unqualified lookup in the definition context, is the only one visible for all instantiations of the closure type's member function template specializations. Clang's error message, that there's no viable conversion to int, is correct and required - the two quotes above regarding ill-formed NDR and undefined behavior don't apply, since the instantiation context doesn't influence the result of name lookup in this case.
Even if we move bar's namespace-scope definition above main, the code still doesn't compile, for the same reason as above: plain unqualified lookup stops when it finds the block-scope declaration void bar(int); and ADL is not performed.
If we consider ADL disabled only in the definition context, then:
As far as the instantiation context is concerned, we're back to the first example; ADL still can't find the namespace-scope definition of bar. The two quotes above (ill-formed NDR and UB) do apply however, and so we can't blame a compiler for not issuing an error message.
Moving bar's namespace-scope definition above main makes the code well-formed.
This would also mean that ADL in the instantiation context is always performed for dependent names, unless we have somehow determined that the expression is not a function call (which usually involves the definition context...).
Looking at how [temp.dep.candidate]/1 is worded, it seems to say that plain unqualified lookup is performed only in the definition context as a first step, and then ADL is performed according to the rules in [basic.lookup.argdep] in both contexts as a second step. This would imply that the result of plain unqualified lookup influences this second step as a whole, which makes me lean towards the first option.
Also, an even stronger argument in favor of the first option is that performing ADL in the instantiation context when either [basic.lookup.argdep]/3.1 or 3.3 apply in the definition context doesn't seem to make sense.
Still... it may be worth asking about this one on std-discussion.
All quotes are from N4713, the current standard draft.
From unqualified lookup rules ([basic.lookup.unqual]):
For the members of a class X, a name used in a member function body, [...], shall be declared in one of the
following ways
— if X is a local class or is a nested class of a local class, before the definition of class X in a block
enclosing the definition of class X
Your generic lambda is a local class within main, so to use bar, the name bar must appear in a declaration beforehand.

Declare function after template defined

Let's say I have a template function:
template <class T>
void tfoo( T t )
{
foo( t );
}
later I want to use it with a type, so I declare/define a function and try to call it:
void foo( int );
int main()
{
tfoo(1);
}
and I am getting error from g++:
‘foo’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
foo( t );
why it cannot find void foo(int) at the point of instantiation? It is declared at that point. Is there a way to make it work (without moving declaration of foo before template)?
foo in your case is a dependent name, since function choice depends on the type if the argument and the argument type depends on the template parameter. This means that foo is looked up in accordance with the rules of dependent lookup.
The difference between dependent and non-dependent lookup is that in case of dependent lookup ADL-nominated namespaces are seen as extended: they are extended with extra names visible from the point of template instantiation (tfoo call in your case). That includes the names, which appeared after the template declaration. The key point here is that only ADL-nominated namespaces are extended in this way.
(By ADL-nominated namespace I refer to namespace associated with function argument type and therefore brought into consideration by the rules of dependent name lookup. See "3.4.2 Argument-dependent name lookup")
In your case the argument has type int. int is a fundamental type. Fundamental types do not have associated namespaces (see "3.4.2 Argument-dependent name lookup"), which means that it does not nominate any namespace through ADL. In your example ADL is not involved at all. Dependent name lookup for foo in this case is no different from non-dependent lookup. It will not be able to see your foo, since it is declared below the template.
Note the difference with the following example
template <class T> void tfoo( T t )
{
foo( t );
}
struct S {};
void foo(S s) {}
int main()
{
S s;
tfoo(s);
}
This code will compile since argument type S is a class type. It has an associated namespace - the global one - and it adds (nominates) that global namespace for dependent name lookup. Such ADL-nominated namespaces are seen by dependent lookup in their updated form (as seen from the point of the call). This is why the lookup can see foo and completes successfully.
It is a rather widespread misconception when people believe that the second phase of so called "two-phase lookup" should be able to see everything that was additionally declared below template definition all the way to the point of instantiation (point of the call in this case).
No, the second phase does not see everything. It can see the extra stuff only in namespaces associated with function arguments. All other namespaces do not get updated. They are seen as if observed from the point of template definition.

In what situations Argument Dependent name Look-up (ADL) kicks in?

In Wikipedia article below quote is mentioned:
ADL only occurs if the normal lookup of an unqualified name fails to
find a matching class member function. In this case, other namespaces
not considered during normal lookup may be searched where the set of
namespaces to be searched depends on the types of the function
arguments.
So, I was expecting below program would compile fine, but it doesn't:
namespace N1 {
class A {};
void foo (A *p) {}
}
namespace N2 {
void foo (N1::A &p) {}
}
int main () {
N1::A xa;
foo(&xa); // ok
foo(xa); // error: cannot convert ‘N1::A’ to ‘N1::A*’ for argument ‘1’ to ‘void N1::foo(N1::A*)’
}
I searched several questions in SO, but couldn't find which lists the requirements or situations in simple word which suggests: When the ADL kicks in ?
A little more detailed answer would be really helpful to me and future visitors.
It shouldn't compiles. A is in namespace N1. How compiler should knows, that you want to call N2::foo?
n3376 3.4.2/2
For each argument type T in the function call, there is a set of zero or more associated namespaces and a
set of zero or more associated classes to be considered. The sets of namespaces and classes is determined
entirely by the types of the function arguments (and the namespace of any template template argument).
Typedef names and using-declarations used to specify the types do not contribute to this set. The sets of
namespaces and classes are determined in the following way:
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
of which its associated classes are members. Furthermore, if T is a class template specialization,
its associated namespaces and classes also include: the namespaces and classes associated with the
types of the template arguments provided for template type parameters (excluding template template
parameters); the namespaces of which any template template arguments are members; and the classes
of which any member templates used as template template arguments are members. [ Note: Non-type
template arguments do not contribute to the set of associated namespaces. — end note ]
ADL kicks in pretty much always, as soon as your function takes user a user defined type. It's kicking in for foo here: xa is defined in N1, so foo is searched in N1 as well as in the global namespace. (Without ADL, foo would only be searched in the global namespace.)
And I don't see why you would expect the second call to foo to compile. The type of xa is defined in N1, so ADL adds N1 to the search path, but there's nothing in the expression to imply N2.
It says "other namespaces" are searched. It doesn't say "all namespaces" are searched.
The rules for what extra namespaces are included in ADL are a little complicated, but the most important one is the namespace in which A is defined. That's why your first foo is found. Your second foo can't be found because namespace N2 is nothing to do with anything, and it is not searched by ADL.
If unqualified Name Look-up fails, then look-up proceeds using the argument of the function's call.
Example
func(A x);
Then compiler will look at namespace up starting at namespace including class A. One example is this
// argument_dependent_name_koenig_lookup_on_functions.cpp
namespace A
{
struct X
{
};
void f(const X&)
{
}
}
int main()
{
// The compiler finds A::f() in namespace A, which is where
// the type of argument x is defined. The type of x is A::X.
A::X x;
f(x);
}
More here
http://msdn.microsoft.com/en-us/library/60bx1ys7.aspx
The compiler stops lookup once he has found a function with a matching name. It does not continue searching if the argument types or accessibility (public/protected/private) actually prevent using the function in the current context. Hence in your example, the compiler has no change to "see" N2::foo, since N1::foo is found first.
Note that in your example, N2::foo wouldn't be found even if N1::foo did not exist, as you have no reference to N2 anywhere inside main, so N2 will not be searched at all.

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