Clang 21 skips template candidates when an exact overload is available
Avoiding unnecessary template instantiation changes which diagnostics appear during overload resolution. P3606 explains why that implementation strategy is visible in real programs.
An overload that cannot win can still cause a compilation error if forming its template candidate instantiates invalid code. That makes the order of compiler work observable. Clang 21 changes this behavior when a non-template overload is a perfect match, avoiding template instantiation that would only produce a losing candidate and potentially a diagnostic.
The LLVM 21.1.0 release appeared on August 26. Its tagged Clang release notes say the compiler implements the changes described in the first two sections of P3606. When a non-template candidate matches with identity conversions for all arguments, template candidates are not instantiated. Diagnostics from that instantiation consequently disappear.
This is a useful example of a compiler optimization that also affects source acceptance. Doing less work is not merely an internal speed improvement if the omitted work would have failed. The explanation needs to identify the candidate being skipped and why the remaining candidate already settles the relevant overload choice.
The losing candidate can have a hard error
Corentin Jabot and Younan Zhang's P3606R0, dated January 24, starts with the existing difference between GCC and other implementations. GCC can select an exact non-template match without performing template argument deduction for a competing template. Other implementations historically attempted that deduction first.
The paper's example places a dependent expression in a lambda used in a template's return type. The non-template overload handles int directly. Calling the function with an integer can therefore select the ordinary overload, but attempting the template candidate can instantiate a lambda body referring to a nonexistent member of int.
That failure is outside the immediate substitution context. It is consequently a hard error, not simply a reason to remove the candidate through ordinary substitution failure. The programmer can receive an error from code belonging to a candidate that would not be selected.
P3606 argues that skipping this work has several benefits, including less compiler work and fewer disruptions when implementing other overload-resolution corrections. Clang's release note identifies the implemented behavior directly. It does not give a numerical compile-time improvement, so the change should not be assigned a benchmark result without one.
I accept the compile-time cost of templates when the abstraction provides a clear runtime benefit. This particular change interests me because it removes work whose only visible result can be a confusing failure in an unused candidate. The improvement concerns both the amount of instantiation and the relevance of the diagnostic to the call being compiled.
Concepts make the difference visible too
P3606 gives a second example involving a converting constructor constrained by std::copyable<T> and a defaulted copy constructor. Checking whether the enclosing type is copyable can require resolving a call to its constructor. If evaluating the converting constructor's constraint asks the same copyability question again, the constraint can depend on itself.
An exact non-template match can avoid entering that recursion. The paper explains that this is why a program can compile in GCC while failing elsewhere, even without the lambda-based hard error of the first example. The order in which candidates are formed affects which constraints the compiler tries to satisfy.
The paper also identifies a more explicit source design. A converting constructor can exclude the enclosing type before checking the broader copyability requirement. That states the intended domain instead of relying entirely on a compiler's decision to skip a candidate.
The compiler change does not make every self-dependent constraint meaningful. Its stated condition is a perfect non-template match. An overload set made entirely of templates does not acquire that escape merely because one template appears intuitively preferable to a reader.
Identity conversions define the boundary
The release note uses a precise definition of perfect match. Every argument conversion sequence must be an identity conversion for the non-template candidate. A conversion that changes the argument type is outside the described case. Summarizing the rule as always preferring ordinary functions over templates would therefore be incorrect.
Likewise, the implementation change concerns candidate formation and the resulting diagnostics. It does not erase templates from overload resolution generally. Where the stated condition is absent, deduction and constraint checking still contribute to determining the viable candidates.
This boundary helps when interpreting a newly successful build. The source may now avoid instantiating a problematic candidate, which is the intended new behavior. It does not follow that the candidate's body has become valid for arbitrary template arguments or that the same call must work under every older compiler.
The same release removes -frelaxed-template-template-args and its negative form. Clang says improvements to template-template parameter matching in the previous release made the flags unnecessary. That is a separate template-related compatibility change, so keeping an old switch is not a way to select the earlier overload behavior described here. A build configuration can require cleanup for a removed option while its source also benefits from the new candidate-instantiation strategy. Both appear during a compiler upgrade, but they need separate explanations.
Existing defect work motivated the timing
P3606 discusses CWG2369, which concerns ordering between constraints and substitution. Implementing that correction exposed programs whose acceptance depended on different compilers failing or skipping candidate work for different reasons. A change that is locally more faithful to one rule can therefore reveal another implementation-policy difference.
The authors use that interaction to ask for clearer intent about allowed strategies. Their paper's wording section remains unfinished, so it is a direction paper explaining the issue rather than a complete replacement standard clause. Clang 21 nonetheless documents the concrete behavior it has implemented from that discussion.
I find that distinction useful when reading C++ release notes. A named paper can explain motivation while the compiler notes define the actual implemented scope. Here the practical result is specific. A perfect non-template match prevents unnecessary template-candidate instantiation, and errors that would have arisen only inside that skipped work no longer determine whether the call compiles.