具有多个包的参数包匹配规则

Parameter Pack Matching Rules with Multiple Packs

本文关键字:参数 包匹配 规则      更新时间:2023-10-16

我正在尝试编写一个函数,该函数使用参数包和一些标准匹配规则来获取另一个函数。举个例子:

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

为了消除TListElemsTVectorElems的歧义,我添加了一些std::tuple<T...>*以便调用者可以显式

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很高兴地以我期望的方式编译它,而g++(7.2.1)给出了编译错误:

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);
^

main中,我希望foo的调用推断出TListElems<int, unsigned>TVectorElems<float, double>,导致fn属于void (*)(std::list<int>, std::list<unsigned>, std::vector<float>, std::vector<double>)类型(当只有一个包或我手动编写重载时的操作方式)。

§14.8.2.5/10 是标准最接近明确阻止foo示例起作用的:

[注意:函数参数包只能出现在参数声明列表(8.3.5)的末尾。

std::list<TListElems>...一点fn似乎违反了这个笔记,但这并不完全清楚。

问题是:谁是对的?海湾合作委员会、叮当还是别的什么?

我认为clang就在这里。

void (*)(std::list<TListElems>..., std::vector<TVectorElems>...)中,TListElems...是一个非演绎的语境,这使得TVectorElems...也是一个非演绎的语境。但是这两个参数包都可以从两个元组指针参数中推导,并且预计它也可以在这里使用该推导结果。

我提交了 gcc 错误 83542。

你可以让两个编译器都对不可推导类型感到满意:

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
}

演示