8

Consider the following code (Godbolt):

#include <tuple>
#include <type_traits> // std::false_type, std::true_type


template <typename t_tuple, auto t_size = std::tuple_size_v<t_tuple>>
struct test : std::false_type {};

template <typename t_tuple>
struct test<t_tuple, 0> : std::true_type {};


int main(int argc, char **argv)
{
    static_assert(test<std::tuple<>>::value);
    return 0;
}

Clang and some other compilers accept the code but in GCC and ICC the static assertion fails. Either changing the type of t_size from auto to std::size_t or casting the zero in the specialisation to std::size_t alleviates the issue. What I would like to know is:

  1. Should I be able to pass the zero in the specialisation as int instead of std::size_t when setting the type of the second template parameter to auto? Obviously (?) the types do not match (since the type of std::tuple_size_v is std::size_t) but I was expecting the compiler to at least warn me about something.
  2. Is there a best practice for using auto with non-type template parameters?

(I checked this question but it had to do with const/volatile.)

TNA
  • 2,595
  • 1
  • 14
  • 19
tsnorri
  • 1,966
  • 5
  • 21
  • 29
  • 1
    https://godbolt.org/z/c9PM9q4a1 – Marek R Dec 19 '22 at 16:16
  • *"but I was expecting the compiler to at least warn me about something."* For what?, you might specialize also for any `test`, (template doesn't have to be called with default (i.e `test, 42>`). – Jarod42 Dec 19 '22 at 16:19
  • 1
    Adding extra specialization (for `std::size_t(0)`) makes instantiation ambiguous for clang/msvc [Demo](https://godbolt.org/z/hea7qs6s4) – Jarod42 Dec 19 '22 at 16:23
  • Off topic: simplified version just works: https://godbolt.org/z/PY3Wbnh9h – Marek R Dec 19 '22 at 16:25
  • That is intresting. Dropping `auto` in favor of `std::size_t` fixes the issue: https://godbolt.org/z/EnbfK5EYh on all compilers. – Marek R Dec 19 '22 at 16:30
  • @Jarod42 I thought that varying the type of a non-type template parameter would not be possible and hence the compiler would warn me (but citation needed). – tsnorri Dec 19 '22 at 16:36
  • Based on this example: https://godbolt.org/z/KbMGTeodE I would say gcc is right here. – Marek R Dec 19 '22 at 16:40
  • @tsnorri not sure what you mean by *"varying the type of a non-type template parameter would not be possible"*, you can definitely specify **any** parameter as `t_size`. – apple apple Dec 19 '22 at 16:40
  • 2
    With full specialization of `auto`, all compilers agree on behavior [Demo](https://godbolt.org/z/1W9rjevzs). With partial, they disagree [Demo](https://godbolt.org/z/W4br3q5Mv) – Jarod42 Dec 19 '22 at 16:41
  • 2
    If I recall correctly, using `auto` as the type of a non-type template parameter effectively introduces a hidden unnamed type parameter, that has to be deduced from the non-type template argument. Roughly, you have `template > struct test;` Then, `struct test` specializes `test`, but `test>` invokes `test, size_t, 0>` – Igor Tandetnik Dec 19 '22 at 16:44
  • Thank you all for your comments. In @Jarod42’s [example](https://godbolt.org/z/W4br3q5Mv), it looks to me that GCC is correct when partial specialisation is used, is this the case? – tsnorri Dec 19 '22 at 17:10
  • 1
    *"Restore"* deleted comment: related [CWD issue #1647](https://cplusplus.github.io/CWG/issues/1647) [Demo](https://godbolt.org/z/so6xaqKGs). – Jarod42 Dec 19 '22 at 20:43
  • 1
    @Jarod42 change `auto t_size = std::tuple_size_v` to `std::size_t t_size = std::tuple_size_v`, and the OP problem won't reproduce. So this is not because `0` is of `int` type which is not `size_t` – Language Lawyer Dec 20 '22 at 09:13

0 Answers0