The entire point of the matter is ADL kicking in:
N3797 - [basic.lookup.argdep]
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.
following:
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 are determined in the following way:
- If T is a class type [..] its associated classes are: ...
furthemore 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
D<A>
is an associated class and therefore in the list waiting for its turn.
Now for the interesting part [temp.inst]/1
Unless a class template specialization has been explicitly instantiated (14.7.2) or explicitly specialized (14.7.3),
the class template specialization is implicitly instantiated [...] when the completeness of the class type affects the semantics of the program
One could think that the completeness of the type D<A>
doesn't affect at all the semantic of that program, however [basic.lookup.argdep]/4 says:
When considering an associated namespace, the lookup is the same as the lookup performed when the associated namespace is used as a qualifier (3.4.3.2)
except that:
[...]
Any namespace-scope friend functions or friend function templates declared in associated classes are visible within their respective
namespaces even if they are not visible during an ordinary lookup (11.3)
i.e. the completeness of the class type actually affects friends declarations -> the completeness of the class type therefore affects the semantics of the program.
That is also the reason why your second sample works.
TL;DR D<A>
is instantiated.
The last interesting point regards why ADL starts in the first place for
u = v; // Triggers ADL
u.operator=(v); // Doesn't trigger ADL
§13.3.1.2/2 dictates that there can be no non-member operator=
(other than the built-in ones). Join this to [over.match.oper]/2:
The set of non-member candidates is the result of the unqualified lookup of operator@ in the context
of the expression according to the usual rules for name lookup in unqualified function calls (3.4.2)
except that all member functions are ignored.
and the logical conclusion is: there's no point in performing the ADL lookup if there's no non-member form in table 11. However [temp.inst]p7 relaxes this:
If the overload resolution process can determine the correct function to call without instantiating a class template definition, it is unspecified whether that instantiation actually takes place.
and that's the reason why clang triggered the entire ADL -> implicit instantiation
process in the first place.
Starting from r218330 (at the time of writing this, it has been committed a few minutes ago) this behavior was changed not to perform ADL for operator=
at all.
References
Thanks to Richard Smith and David Blaikie for helping me figuring this out.