8

I think I hit a template type deduction bug in all the compilers, but before reporting it I want to be sure I did not miss something.

Consider an example:

#include <utility>

template <std::size_t... I, typename... T>
void foo(std::index_sequence<I...>, decltype(I)..., T...) {}

int main()
{
    foo(std::make_index_sequence<3>{}, 1, 2, 3, 4, 5);
}

decltype(I) is used here to make MWE shorter.

  • GCC: error: too few arguments to function
  • Clang crashes with error: no matching function for call to 'foo'
  • MSVC crashes with error C3543: 'unknown-type': does not contain a parameter pack

I don't understand why it fails to deduct T, especially because it works if I replace parameter pack with varargs (except MSVC, it has ICE again).

template <std::size_t... I>
void foo(std::index_sequence<I...>, decltype(I)..., ...) {}

There are many other ways to make what I want, but this is the shortest way and I do not see any reasons it should fail.

Update: The know valid example with substitution on deducible is:

template <typename T>
struct type_identity
{ using type = T; };

template <typename T, typename... U>
void foo(T, typename type_identity<T>::type, U...) {}

int main()
{
    foo(1, 2, 3, 4, 5);
}

Update #2 The modified version of the original example does not crash Clang, but a note on the error is strange note: candidate template ignored: deduced conflicting types for parameter 'T' (<int, int, int> vs. <>)

#include <utility>

template <typename T>
struct type_identity
{ using type = T; };

template <typename...>
struct type_pack {};

template <typename... T, typename... U>
void foo(type_pack<T...>, typename type_identity<T>::type..., U...) {}

int main()
{
    foo(type_pack<int, int, int>{}, 1, 2, 3, 4, 5);
}
Nikita Kniazev
  • 3,728
  • 2
  • 16
  • 30
  • 2
    For the record, it's not a compiler "crash". That would imply the compiler ran into an internal error, not one in your code. – Max Langhof Oct 10 '18 at 17:45
  • 3
    Actually [clang indeed crashes](https://wandbox.org/permlink/4L6IKjuaE2JPv1l3)... – user7860670 Oct 10 '18 at 17:46
  • After trying to make simplified example `template void foo() {} int main() { foo<0, int>(); }` work in VS it produced surprisingly understandable message: `error C3547: template parameter 'T' cannot be used because it follows a template parameter pack and cannot be deduced from the function parameters of 'foo'` – user7860670 Oct 10 '18 at 18:11
  • @VTT you are trying to do something other, there is not deduction in your code. The code you have posted is invalid without any doubt. – Nikita Kniazev Oct 10 '18 at 18:15
  • I think the problem is essentially the same: `T...` can not be deduced properly. Compiler can not figure out `I...` size just from first argument and then figure out which of the remaining arguments are for `decltype(I)…` and which are for `T...`. – user7860670 Oct 10 '18 at 18:21

1 Answers1

4

I think your code is ill-formed by the standard, but an argument could be made that the standard should be changed to make it well-formed.

The template argument deduction process is described in the standard as follows:

  1. Explicitly specified template arguments are substituted into the function template declaration ([temp.deduct]/2-5).
  2. Remaining template parameters that appear in deduced contexts in the parameter-type-list are deduced ([temp.deduct.call]).
  3. The deduced template arguments, together with template parameters that have default template arguments, are substituted into the function declaration [temp.deduct.call]/10. This is the end of the deduction process, and is followed by overload resolution.

Thus, the procedure is substitution-deduction-substitution; there is no subsequent deduction after the substitution of deduced arguments. Further support for this view is provided by [temp.deduct]/6:

At certain points in the template argument deduction process it is necessary to take a function type that makes use of template parameters and replace those template parameters with the corresponding template arguments. This is done at the beginning of template argument deduction when any explicitly specified template arguments are substituted into the function type, and again at the end of template argument deduction when any template arguments that were deduced or obtained from default arguments are substituted.

Your example is ill-formed because it requires deduction then substitution then deduction: T... cannot be deduced unless I... is first both deduced and substituted.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • Well, I am not sure that `std::index_sequence` needs to be deduced, the `I...` pack is known at the first substitution phase. Anyway, thanks for the reply, I will consider writing a Defect Report. (However, I do not have an idea how the process is initiated). – Nikita Kniazev Oct 10 '18 at 18:36
  • 1
    @NikitaKniazev I don't understand your comment. Of course `I...` needs to be deduced. I don't get it. Also, I'm not sure this would be appropriate for a DR---I think you need to write a full proposal. Write up some suggested wording changes and send them to std-proposals@isocpp.org – Brian Bi Oct 10 '18 at 18:43
  • I have updated the question with the example where there is a substitution on deducible type. They are both should be valid or invalid, but the latter one is a known thing. – Nikita Kniazev Oct 10 '18 at 18:48
  • @NikitaKniazev I think your second example is different because it's possible to deduce `U...` before `T` has been substituted. – Brian Bi Oct 10 '18 at 18:50
  • The example shows that either "deduction then substitution then deduction" can happen or something other is happening here. – Nikita Kniazev Oct 10 '18 at 18:54
  • @NikitaKniazev I disagree. `T` and `U...` are both deduced, then they are both substituted. – Brian Bi Oct 10 '18 at 18:56
  • I agree with Brian here - in the original case you have two parameter packs, in the new case you have one. Those situations are not the same. Just work out on paper what you have to do for both cases. – Barry Oct 10 '18 at 19:02
  • "_an argument could be made that the standard should be changed to make it well-formed._" Could you elaborate on how this argument might look, in terms of what could be changed and whether there are enough use cases to justify it, or were you just speculating that someone else like the OP might wish to make it? – underscore_d Oct 13 '18 at 17:06
  • 1
    @underscore_d I think the current behaviour is unintuitive, and if I had some time, I might be able to come up with some wording to say that deduction and substitution should alternate until there are no more deduced contexts left. But before writing a proposal I think we'd want to consider whether this would fix any cases other than the one in this question. – Brian Bi Oct 16 '18 at 02:51