5
votes

(This question is a branch-out from the discussion in the comments of Template specialization of variable template and type deduction.)


[temp.expl.spec]/10 states that [emphasis mine]:

A trailing template-argument can be left unspecified in the template-id naming an explicit function template specialization provided it can be deduced from the function argument type. [ Example:

template<class T> class Array { /* ... */ };
template<class T> void sort(Array<T>& v);

// explicit specialization for sort(Array<int>&)
// with deduced template-argument of type int
template<> void sort(Array<int>&);

 — end example ]

Which clearly applies to the (full) explicit specialization of foo(T) in the example below:

#include <iostream>

template <typename T>
void foo(T) { std::cout << "primary\n"; }

template <>
void foo(int) { std::cout << "int\n"; }
// OK   ^<int> deduced from template argument deduction
//             in this _declaration_, as of [temp.expl.spec]/10

int main()
{
    const int a = 42;
    foo(a);  // int
    // OK, <int> deduced from template argument deduction.
}

However, for both clang and GCC, for all the various ISO C++ version I have tested, this also applies in the example where there are no function arguments in the function template, and where its, say, type template parameter is solely present as the function template's return type:

#include <iostream>

template <typename T>
T bar() { std::cout << "primary\n"; return 0; }

template <>
int bar() { std::cout << "int\n"; return 42; }
//     ^<int> deduced?

int main()
{
    (void)bar<int>();  // int
    //        ^^^ OK, no template argument deduction.
}

I'm slightly confused with term "deduced" in the quote above, as it, afaics, doesn't refer to deduction in the sense of the typical (call site/instantiation) template argument deduction, but rather deduction in the context of a specialization declaration.

Question:

  • Where is it covered in the ISO C++ Standard that the trailing template-argument in a declaration of an explicit function template specialization with a template argument only present as the return type, may in fact be omitted (deduced)?
1
Could [temp.over.link]/4 have any relevance?dfrib
No, there are no expressions involving template parameters in any of these, just types involving template parameters.aschepler

1 Answers

4
votes

I think [temp.expl.spec]/10 has bad wording in saying "function argument type" instead of just "function type", which is what actually gets used in template argument deduction for an explicit specialization of a function template. (Plus of course, there could be more than one function argument type.)

Different contexts specify different sets of dependent types (P) and specified (often non-dependent) types (A) to be used in template argument deduction, as listed in subsections [temp.deduct.call], [temp.deduct.funcaddr], [temp.deduct.conv], [temp.deduct.partial], and [temp.deduct.decl].

The most familiar case is probably the first of these, [temp.deduct.call]: When calling a function template, the function parameter types are P and the types of the argument expressions are A, but the return type of the function template does not participate.

The last of these, [temp.deduct.decl], covers matching for explicit specializations of a function template (as in your examples), explicit instantiations of a function template, and friend declarations which befriend a specific specialization of a function template. For these cases, the entire function types are P and A. Since a function type counts as a "compound type" formed from the return type and argument types, similarly to how a pointer type T* is formed from the type T, this allows deduction of template parameters appearing in the return type and/or in the argument types.