There are several problems with this, and one of them leads to the error.
template <typename Inner1, typename T2>
auto
join(const wrapper<Inner1>& w1, const T2& value2) // (A)
-> wrapper<decltype(join(w1.value, value2))>;
The name lookup of join
here will not find the same function template via unqualified lookup, as the trailing-return-type is part of the declaration, and names can only be found once they have been declared. But the syntax allows ADL to find the same function template. ADL for dependent names is performed later (from the point of instantiation).
As far as I understood the problem, the issue comes from overload resolution: Before decltype(join(w1.value, value2))
tries to resolve to an overload, all function templates with that name need to be instantiated. For each function template, one instantiation is added to the overload set (if instantiation succeeds).
Therefore, all join
s need to be instantiated. Instantiation includes determining the return type. For every instantiation of this particular join
function template (A), the same function template (A) with the same template arguments is a candidate for the overload resolution set. That is, to determine which return type (A) has, there needs to be an overload resolution, which requires to determine the return type of (A) and so on.
Deduction & substitution never fails in any single step of the recursion, the only reason not to choose this overload is partial ordering between the different function templates called join
. And partial ordering is only checked as part of the overload resolution process -- which is too late to prevent further instantiations.
This error, as mentioned in the error message, occurs as an implementation limit. Therefore, it does not fall into the SFINAE category, see Solving the SFINAE problem for expressions. Therefore, even if this overload is not chosen, its mere existence makes the program ill-formed, just like
struct tag_for_ADL {};
template<class T>
auto foo(T p) -> decltype(foo(p));
foo(tag_for_ADL{}); // ill-formed, leads to infinite recursive instantiation