多个参数包匹配规则

6
我正在尝试编写一个函数,该函数使用参数包和一些标准匹配规则来接受另一个函数。以下是一个例子:
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>) (当只有一个 pack 时或者如果我手动编写了 overload 时的操作方式)。

§14.8.2.5/10 是标准所做的最接近明确阻止 foo 示例工作的地方:

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

看起来 fn 中的 std::list<TListElems>... 部分似乎会违反这个注意事项,但这并不完全清楚。

问题是:谁是正确的?GCC、Clang 还是其他东西?

2个回答

3

§17.9.2.5/6提供了Jarod42解决方案奏效的原因(具体而言:“如果将类型指定为void f(typename A<T>​::​B, A<T>),则A<T>::B中的T是无法推导的,但A<T>中的T是可以推导的”),但是对于Clang或GCC是否实际具有正确的解释,我感到不确定。 - Travis Gockel

2
您可以使用非可推断类型来使编译器同时满意:
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


我会在函数指针本身上使用 non_deducible_t。所以,non_deducible_t<void (std::list<TListElems>...,std::vector<TVectorElems>...)>* - Yakk - Adam Nevraumont

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接