Search code examples
c++templatesparameter-pack

Parameter pack works in msvc but not in gcc and clang


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.


Solution

  • 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.