Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variadic template type deduction crashes compilers if there is a substitution on deducible type

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);
}
like image 979
Nikita Kniazev Avatar asked Oct 10 '18 17:10

Nikita Kniazev


1 Answers

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.

like image 133
Brian Bi Avatar answered Nov 13 '22 20:11

Brian Bi