8

Is the deduction for f1 and f2 ill-formed?

template<class... T, class U>
void f1(T..., U){}

template<class... T>
void f2(T..., int){}

int main()
{
    f1(1);
    f2(1);
    return 0;
}

g++ accepts both, clang only accepts f2, and msvc rejects both.

Related standard wording:

[temp.deduct.call]

When a function parameter pack appears in a non-deduced context ([temp.deduct.type]), the type of that parameter pack is never deduced.

[temp.deduct.type]p5

The non-deduced contexts are:

  • A function parameter pack that does not occur at the end of the parameter-declaration-list.

So it seems that MSVC is correct in rejecting both?

Does it mean that any instantiation of the templates will be ill-formed, even if you specify the template args explicitly?

f1<int>(1, 2); // ill-formed?
f2<int>(1, 2); // ill-formed?

If that's the case, why allow such declarations at first place?

Jamboree
  • 5,139
  • 2
  • 16
  • 36

2 Answers2

2

There's a DR for this specific issue DR1388. Aparently, it seems that GCC and CLANG haven't implemented it yet CLANG DR1388.

Does it mean that any instantiation of the templates will be ill-formed, even if you specify the template args explicitly?

f1<int>(1, 2); // ill-formed?
f2<int>(1, 2); // ill-formed?

If that's the case, why allow such declarations at first place?

No if you specify explicitly the template arguments, no deduction occurs and as such the code showed above is legal.

101010
  • 41,839
  • 11
  • 94
  • 168
  • I think the resolution is still flawed - a parameter pack is conceptually infinite, you can't explicitly specify *all* the arguments, that means there's always parameters need to be deduced, and since it's in a non-deduced context, the rest of parameter pack is never deduced. – Jamboree Apr 15 '16 at 09:06
  • @Jamboree You said it your self "it's a non-deduced context" however if you specify the arguments of the pack explicitly no deduction occurs. – 101010 Apr 15 '16 at 09:09
  • Are these considered explicitly specified: `f1<>(1); f2<>(1);`? Does `<>` make any difference here? – Jamboree Apr 15 '16 at 09:16
  • 1
    `<>` is an empty argument list, nevertheless is an argument list. It will resolve the pack to an empty pack. – 101010 Apr 15 '16 at 09:23
  • So are you suggesting that `f1(1)` is ill-formed while `f1<>(1)` is ok? – Jamboree Apr 15 '16 at 09:25
  • After seeing [DR1399](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1399), it's clear that both `f1(1)` and `f1<>(1)` should be ok, so GCC is correct in this regard. – Jamboree Apr 15 '16 at 09:34
  • @Jamboree DR1399: This issue is resolved by the resolution of issue 1388. – 101010 Apr 15 '16 at 09:37
  • If you could update your answer to make it clear that all my examples are well-formed and GCC is the correct one, I can accept this answer and remove the discussion above. – Jamboree Apr 15 '16 at 09:43
0

I've looked into this exact question myself and I've found it surprisingly difficult to get a clear answer. As best I can tell, f1(1) should be rejected but f2(1) should be accepted.

That said:

  • clang++-5.0 accepts both
  • g++-6 accepts both
  • EDG 4.14 rejects both

As you pointed out, a function parameter pack that does not occur at the end of the list is a non-deduced context ([temp.deduct.type] p5):

The non-deduced contexts are:

  • ...
  • A function parameter pack that does not occur at the end of the parameter-declaration-list.

and [temp.deduct.call] p1 (amended via CWG 1388) clarifies that such a parameter pack is never deduced.

When a function parameter pack appears in a non-deduced context, the type of that parameter pack is never deduced.

Additionally, [temp.arg.explicit] p3 specifies:

A trailing template parameter pack (14.5.3) not otherwise deduced will be deduced to an empty sequence of template arguments.

so, taking this into account for the f2(1) call: the pack T is a trailing template parameter pack (although it is not a trailing function parameter pack), so it deduced to an empty pack and the call is valid.

However, for f1(1), the pack T is also not a trailing template parameter pack because it is followed by U, so it is not assumed to be an empty pack per [temp.arg.explicit] p3. Therefore, since the template paramter pack T cannot be deduced for the call to f1(1), it should not take part in overload resolution and the call should fail.


Note that several similar questions/examples have been brought up in other discussions, but that they are all subtly different:

  • The f(0) call in the example code of CWG 1388 and CWG 1399 is valid because the pack in question is a trailing template parameter pack, so it falls into the case I mentioned above. Here is the code from CWG 1399:
    template <class... T>
    void f(T..., int, T...) { }
    
    int main() {
      f(0);          // OK
      f<int>(0,0,0); // OK
      f(0,0,0);      // error
    }
    
  • The example code in the discussion of LLVM bug 21774 is similar to the CWG 1399 example, the pack is a trailing template parameter pack. Likewise for this question.
  • CWG 2055, which is unresolved, touches upon a similar test case. Whenever that gets resolved, its resolution will likely shed some light as to the proper behavior of the examples in this question. Here is the issue mentioned in CWG 2055:

    It is not clear that [the current wording of the standard] permits an example like:

    template<typename... T> void f(typename T::type...)   {
    }
    
    int main() {
      f<>();
    }
    
Chris Badger
  • 121
  • 3