3

Consider the following example:

#include <iostream>

template <class T, int V>
struct S
{
    friend int Func(T) // decl-1
    {
        return V;
    }
};

struct U
{
    friend int Func(U); // decl-2
};

template struct S<U, 42>; // spec-1

int main()
{
    std::cout << Func(U{}) << std::endl; // Compiles and prints 42
}

My understanding is that the expression Func(U{}) causes an unqualified name lookup of the function Func, and through ADL finds declaration decl-2. However, this is not a viable candidate function for overload resolution (since it is not defined), and thus the compiler selects declaration decl-1. Misunderstanding, and irrelevant to the question, see comment from @LanguageLawyer

My question is what rule(s) in the standard allow the compiler to use the template parameters from the specialization spec-1 to instantiate the class template that contains decl-1.

Searching through cppreference, the only rule I found that seems to apply refers to overload resolution of function templates, and to cite:

For function templates, template argument deduction and checking of any explicit template arguments are performed to find the template argument values (if any) that can be used in this case:

  • if both succeeds, the template arguments are used to synthesize declarations of the corresponding function template specializations, which are added to the candidate set, and such specializations are treated just like non-template functions except where specified otherwise in the tie-breaker rules;
  • if argument deduction fails or the synthesized function template specialization would be ill-formed, no such function is added to the candidate set.
Source: https://en.cppreference.com/w/cpp/language/overload_resolution#Details

Is decl-1 considered a function template for the purposes of overload resolution? Does the compiler synthesize a declaration template int Func<U, 42>(U) using spec-1 (through template argument deduction, presumably)? Or is something else at play here?

EDIT: An additional misconception that I may have is what exactly is spec-1, my current understanding is that it is the declaration of an explicit specialization of class template S as an incomplete type, if possible please clarify if this is correct.

  • 2
    _this is not a viable candidate function for overload resolution (since it is not defined)_ Since when "not defined" became overload resolution criteria? How to distinguish "not defined" from "defined in another TU"? – Language Lawyer Jul 07 '22 at 15:13
  • decl-1 from spec-1 is just a definition for decl-2. They declare the same function. – Language Lawyer Jul 07 '22 at 15:16
  • @LanguageLawyer Thank you, over the last hours, I've been reviewing my understanding of the language, and indeed decl-2 is just a declaration of some function `Func`, stating it as not defined is not relevant for overload resolution (and indeed, it may be defined elsewhere). But then I figure I may have some misconceptions about exactly what spec-1 is, I understood it as the declaration of an incomplete explicit specialization of S, but from your comment I'm guessing that may not be quite right. I'll edit the question to clear the wrong assumption and reflect these doubts. – Shinuzi Kyura Jul 07 '22 at 15:30
  • 1
    spec-1 is an explicit instantiation. Explicit specializations are introduced by `template <>`. https://timsong-cpp.github.io/cppwp/n4861/temp.explicit#nt:explicit-instantiation https://timsong-cpp.github.io/cppwp/n4861/temp.expl.spec#nt:explicit-specialization – Language Lawyer Jul 07 '22 at 15:34
  • @LanguageLawyer Ok, wow, thanks for clearing this up for me, all this time (probably from not seeing it commonly) I assumed it was just a shorthand for writing `template <>`, but from what I understand now, _spec-1_ seems no different from `template <> struct S { friend int Func(U) { return 42; } }`. If you post that as an answer, I'll gladly accept it. – Shinuzi Kyura Jul 07 '22 at 15:51

2 Answers2

1

An additional misconception that I may have is what exactly is spec-1, my current understanding is that it is the declaration of an explicit specialization of class template S as an incomplete type, if possible please clarify if this is correct.

This is not correct.

template struct S<U, 42>; // spec-1

Is not a specialization at all, but an explicit instantiation.

It just tells the compiler to emit the same instantiation of S with parameters T=U and V=42 that would have been emitted if you referred to S<U, 24> in any other context.

A specialization as an incomplete type looks like

template <> struct S<U, 42>;
Useless
  • 64,155
  • 6
  • 88
  • 132
1

what rule(s) in the standard allow the compiler to use the template parameters from the specialization spec-1 to instantiate the class template that contains decl-1.

This is specified in temp.explicit-12 which states that:

An explicit instantiation definition that names a class template specialization explicitly instantiates the class template specialization and is an explicit instantiation definition of only those members that have been defined at the point of instantiation.

(emphasis mine)

And since spec-1 is an explicit instantiation definition, the result will be the instantiation of the class template specialization S<U, 42>


Is decl-1 considered a function template for the purposes of overload resolution?

No, decl-1 is a definition for an ordinary(non-template) function. Moroever, this decl-1 is nothing but the definition for the function declared in decl-2.

Jason
  • 36,170
  • 5
  • 26
  • 60