7

[temp.arg.explicit]/3 of the C++17 standard (final draft) says about deduction of function template arguments with explicitly specified template argument lists:

In contexts where deduction is done and fails, or [...], if a template argument list is specified and it, along with any default template arguments, identifies a single function template specialization, then the template-id is an lvalue for the function template specialization.

How does this apply to parameter packs?

Consider

template<typename...>
struct S {
    S(int) {}
};

template<typename... A>
void f(S<A...>) {}

int main() {
    f<int>(0);
}

This compiles on MSVC, but not on GCC and Clang, see godbolt. It would also be my intuition that it should fail, because deduction will fail, but the quote above seems to imply that even if deduction fails, since f<int> (in my understanding) identifies uniquely a template specialization, f<int> should be considered to refer to that specialization and then call it, without overload resolution, which will work, implicitly converting 0 to S<int>.

What is wrong in my understanding of the quote or is MSVC indeed correct?


Note that if we try to call f<>(0); (which I guess should work by the considerations above) all three compilers refuse to compile.

walnut
  • 21,629
  • 4
  • 23
  • 59

3 Answers3

3

Relevant for the question is also [temp.arg.explicit]/6 that tells us that implicit conversions on a function parameter (as you want above) are

if the parameter type contains no template-parameters that participate in template argument deduction. [ Note: Template parameters do not participate in template argument deduction if they are explicitly specified. [...] ]

So, now is the question if A... participates in template argument deduction. (At this point I wanto to note that OPs code compiles also under gcc/clang if we replace the parameter pack by one template parameter, as it should since it is explicitly specified).

One could argue that A... is explicitly specified and therefore does not participate in deduction. But I would argue that one is wrong. [temp.arg.explicit]/9 tells us that deduction can extend explicitly specified template argument lists. Hence, f<int>(S<int, char>{0}); is valid and A... is deduced to int, char. So in this case A... definitely participates in deduction. But since this call only differs from your call by the parameter, the deduction also has to take place in your call, too.

In other words f<int>(0); could also mean to call f<int, char> and as such, it does not specify a single function template specification.

n314159
  • 4,990
  • 1
  • 5
  • 20
  • 1
    I didn't notice initially when reading that section that it only says "*participate*". I guess you may be right. – walnut Jan 03 '20 at 12:20
2

It's irrelevant. There's no such thing as a function call "without overload resolution". CWG2092 makes this clear.

[temp.over]/1 controls (broken apart for readability; emphasis mine):

When a call to the name of a function or function template is written (explicitly, or implicitly using the operator notation), template argument deduction ([temp.deduct]) and checking of any explicit template arguments ([temp.arg]) are performed for each function template to find the template argument values (if any) that can be used with that function template to instantiate a function template specialization that can be invoked with the call arguments.

For each function template, if the argument deduction and checking succeeds, the template-arguments (deduced and/or explicit) are used to synthesize the declaration of a single function template specialization which is added to the candidate functions set to be used in overload resolution.

If, for a given function template, argument deduction fails or the synthesized function template specialization would be ill-formed, no such function is added to the set of candidate functions for that template. The complete set of candidate functions includes all the synthesized declarations and all of the non-template overloaded functions of the same name. The synthesized declarations are treated like any other functions in the remainder of overload resolution, except as explicitly noted in [over.match.best].

Community
  • 1
  • 1
T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Ah thanks, that matches my intuition for the behavior much better. So I suppose it is also irrelevant when forming a pointer to function, i.e. `void(*ptr)(int) = f;` doesn't work even if the specialization is chosen, because `ptr` cannot be initialized from the wrong function type? Then the sentence I quoted in the question applies mostly to the remaining context enumerated in [\[temp.arg.explicit\]/3](https://eel.is/c++draft/temp.fct.spec#temp.arg.explicit-3)? And GCC and Clang are in the right to reject the example I gave? – walnut Jan 03 '20 at 19:50
1

There is an open core language issue (issue 2055: Explicitly-specified non-deduced parameter packs) that is well related to this case.

From my understanding of what is implictly stated in this issue, the intent is that compilers should behave as MSVC but the standard is said not to be clear enough.

Oliv
  • 17,610
  • 1
  • 29
  • 72
  • The example in that issue description actually does compile on all three compilers. I think the example also covers only the "*or in contexts where deduction is not done*" of the standard quote that I deliberately left out of the question. It is not clear to me though, how broad the scope of the issue is. – walnut Jan 03 '20 at 11:57
  • @walnut In your case as in the case of the issue template argument deduction fails. In your case this is because 'int' is not of the form of 'Bar', in the case of the issue this is because 'Type::type' is non deduced context. See http://eel.is/c++draft/temp.deduct.type#8 – Oliv Jan 03 '20 at 12:09
  • Ah ok, I see, I guess that makes sense. Somehow I though deduction wouldn't even take place if there is no parameter from which can be deduced, but that doesn't seem right in hindsight. – walnut Jan 03 '20 at 12:18
  • @walnut Yes compiler should try to extend the argument list of parameter pack: https://timsong-cpp.github.io/cppwp/n4659/temp.arg.explicit#9 – Oliv Jan 03 '20 at 12:21
  • I meant when there are *no function parameters* (or pack expansions) from which to deduce, sorry. Obviously parameter packs should be extended. In any case I see that I was wrong. – walnut Jan 03 '20 at 12:23
  • @n314159 linked another paragraph in the standard section that might disallow implicit conversion in the function call even if the template specialization is successfully chosen according to the quote in the question. Incidentally, the example in the core issue description does *not* require an implicit conversion for the function call after determining the template specialization. – walnut Jan 03 '20 at 12:26
  • What is bugging me about that quote is, that it only states that implicit conversions will take place "if" some conditions are met not "only if". So maybe it does not explicitly disallow that. – n314159 Jan 03 '20 at 12:32
  • @walnut Indeed its answer is more pertinent. On the other hand, this specific case may have been overlooked. May you could ask a question on the std discussion mailing list, sure someone involved in the commitee will answer you:https://lists.isocpp.org/mailman/listinfo.cgi – Oliv Jan 03 '20 at 12:34