I recently learnt about parameter packs. Then I wrote the following program that compiles with msvc but not with clang and gcc. Live demo:
#include <array>
template<typename T, std::size_t... rows> int func(T (&arr...)[rows])
{
return 5;
}
int main()
{
int arr[2][3][4];
auto a = func(arr);
}
As can be seen in the above demo, msvc compiles it but gcc says:
<source>:3:56: error: 'arr' was not declared in this scope
3 | template<typename T, std::size_t... rows> int func(T (&arr...)[rows])
| ^~~
<source>:3:69: error: parameter packs not expanded with '...':
3 | template<typename T, std::size_t... rows> int func(T (&arr...)[rows])
| ^
<source>:3:69: note: 'rows'
<source>:6:2: error: expected ';' before 'int'
6 | }
| ^
| ;
7 | int main()
| ~~~
Compiler returned: 1
I want to know which compiler is correct and the reason.
The syntax for a parameter pack has ...
in front of the declarator-id:
template<typename T, std::size_t... rows> int func(T (&...arr)[rows])
{
return 5;
}
This is the same for simpler parameter packs:
template<typename... Ts>
void test(Ts ...arr);
Whitespace doesn't matter and Ts... arr
is a more common style but doesn't reflect well that ...
is part of the declarator ...arr
, not of the type specifier Ts
.
MSVC seems to accept the syntax with ...
after the declarator-id arr
for some reason, but that should be ill-formed. However, MSVC does give it the same meaning as the correct order would, see below.
But it doesn't have the meaning you intent it to have. T (&...arr)[rows]
expands to a list of parameters of the form T (&arr1)[rows1], T (&arr2)[rows2], T (&arr3)[rows3]
and so on. You can't add [rowsN]
syntax to a parameter's type with a pack expansion.
In your call auto a = func(arr);
, T
will be int[3][4]
and rows
will have a single element with value 2
.