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?
I think clang is right here.
In void (*)(std::list<TListElems>..., std::vector<TVectorElems>...)
, TListElems...
is a non-deduced context, which makes TVectorElems...
also a non-deduced context. But both parameter packs are deducible from the two tuple pointer arguments, and it's expected to be able to just use that deduction result here too.
I filed gcc bug 83542.
You may make happy both compilers with non deducible type:
template <typename T>
struct non_deducible {
using type = T;
};
template <typename T> using non_deducible_t = typename non_deducible<T>::type;
template <typename... TListElems, typename... TVectorElems>
void foo(std::tuple<TListElems...>*,
std::tuple<TVectorElems...>*,
void (*)(non_deducible_t<std::list<TListElems>>...,
non_deducible_t<std::vector<TVectorElems>>...))
{
// blah blah blah
}
Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With