C++ Logo


Advanced search

Re: Potential defect in the standard when disambiguating function templates when one of them contains a pack

From: Andrew Schepler <aschepler_at_[hidden]>
Date: Tue, 21 Jun 2022 18:09:27 -0400
I think the code is well-formed, and MSVC is incorrect.

The strange thing in this example is that since class template C has one
non-pack parameter, the second function template involving C<T...> can only
have valid specializations with sizeof...(T)==1. If I change C to be a
variadic class template, "template<typename... T> struct C {};", I notice
MSVC agrees with the other compilers that the code is well-formed.

But I don't see anything which says that this situation using a pack
expansion as the argument for a non-pack parameter is ill-formed, or makes
any difference to the function template partial ordering procedure. The
fact remains that the transformed type from the second function template
has A_1 which was originally a pack expansion and P_1 from the first
function template is not a pack expansion, so deduction of the first f from
the transformed second f fails ([temp.deduct.type]/(9.2), just before the
Example quoted in the OP.). Deduction of the second f from the transformed
first f succeeds. So the first f is more specialized than the second f and
the first f should be selected.

I don't see a need for clarification or more examples in the Standard.

-- Andrew Schepler

On Tue, Jun 21, 2022 at 2:50 PM Anoop Rana via Std-Discussion <
std-discussion_at_[hidden]> wrote:

> Is the example well formed? That is should the call f(C<int>{}) succeed?
> On Sun, 19 Jun 2022, 15:35 Anoop Rana, <ranaanoop986_at_[hidden]> wrote:
>> I was working with parameter packs when I noticed that one such
>> case(given below) doesn't compile in msvc but compiles fine in gcc and
>> clang. Here is the link for the verification of the same: link to demo
>> <https://godbolt.org/z/6hf7Ys1Tz>
>> The code is as follows:
>> template<typename T> struct C{};
>> template<typename T> void f(C<T>){
>> }template<typename... T> void f(C<T...>){
>> }int main(){
>> f(C<int>{}); //Should this call succeed? }
>> I want to know if the above example is well-formed according to the
>> standard. I mean should the call *f(C<int>{}); *succeed by choosing the
>> first overload version `*void f(C<T>)*` over the second version.
>> For possible explanation i looked at examples given in:
>> temp.deduct.type#9.2
>> <https://timsong-cpp.github.io/cppwp/n4861/temp.deduct.type#9.2>:
>> [Example
>> template<class T1, class... Z> class S; // #1template<class T1, class... Z> class S<T1, const Z&...> { }; // #2template<class T1, class T2> class S<T1, const T2&> { }; // #3
>> S<int, const int&> s; // both #2 and #3 match; #3 is more specialized
>> End Example]
>> My current understanding/intuition is that the example that I gave here <https://godbolt.org/z/6hf7Ys1Tz> should be valid as well and the first overload should be chosen over the second.
>> *If so*, should this or a similar example be added in temp.deduct.type to make this more clear?
>> Basically I want to know if the intention for the same(i.e., for allowing the example that i gave) was always there but was not clearly conveyed. If not, then is there a clause that clear disallows
>> the example that i gave which i missed to notice/understand.
>> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion

Received on 2022-06-21 22:09:40