jeudi 21 décembre 2017

Parameter Pack Matching Rules with Multiple Packs

I am trying to write a function which takes another function, using parameter packs and some standard matching rules. As an example:

template <typename... TListElems, typename... TVectorElems>
void goal(void (*fn)(std::list<TListElems>..., std::vector<TVectorElems>...));

In order to disambiguate TListElems and TVectorElems, I added some std::tuple<T...>* so a caller can be explicit:

template <typename... TListElems, typename... TVectorElems>
void foo(std::tuple<TListElems...>*,
         std::tuple<TVectorElems...>*,
         void (*)(std::list<TListElems>..., std::vector<TVectorElems>...))
{
    // blah blah blah
}

void bar(std::list<int>, std::list<unsigned>, std::vector<float>, std::vector<double>)
{
    // blah blah blah
}

int main()
{
    foo((std::tuple<int, unsigned>*) nullptr,
        (std::tuple<float, double>*) nullptr,
        &bar);
}

Clang happily compiles this in the way I would expect, while g++ (7.2.1) gives the compilation error:

matching.cpp: In function ‘int main()’:
matching.cpp:20:13: error: no matching function for call to ‘foo(std::tuple<int, unsigned int>*, std::tuple<float, double>*, void (*)(std::list<int>, std::list<unsigned int>, std::vector<float>, std::vector<double>))’
         &bar);
             ^
matching.cpp:6:6: note: candidate: template<class ... TListElems, class ... TVectorElems> void foo(std::tuple<_Tps ...>*, std::tuple<_Elements ...>*, void (*)(std::list<TListElems>..., std::vector<TVectorElems>...))
 void foo(std::tuple<TListElems...>*,
      ^~~
matching.cpp:6:6: note:   template argument deduction/substitution failed:
matching.cpp:20:13: note:   mismatched types ‘std::vector<TVectorElems>’ and ‘std::list<int>’
         &bar);
             ^

In main, I would expect the call to foo to deduce TListElems as <int, unsigned> and TVectorElems as <float, double>, leading fn to be of type void (*)(std::list<int>, std::list<unsigned>, std::vector<float>, std::vector<double>) (the way things operate when there is only one pack or if I had manually written the overload).

§14.8.2.5/10 is the closest the Standard comes to explicitly preventing the foo example from working:

[Note: A function parameter pack can only occur at the end of a parameter-declaration-list (8.3.5). -end note]

The std::list<TListElems>... bit of fn seems like it would violate this note, but that's not entirely clear.

The question is: Who is right? GCC, Clang, or something else?

Aucun commentaire:

Enregistrer un commentaire