【问题标题】:Parameter Pack Matching Rules with Multiple Packs多包参数包匹配规则
【发布时间】:2018-06-04 15:01:07
【问题描述】:

我正在尝试使用参数包和一些标准匹配规则编写一个接受另一个函数的函数。举个例子:

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

为了消除 TListElemsTVectorElems 的歧义,我添加了一些 std::tuple&lt;T...&gt;* 以便调用者可以明确:

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&lt;int, unsigned&gt;TVectorElems&lt;float, double&gt;,导致fn 为类型void (*)(std::list&lt;int&gt;, std::list&lt;unsigned&gt;, std::vector&lt;float&gt;, std::vector&lt;double&gt;)(只有一包或我手动编写重载时的操作方式)。

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

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

fnstd::list&lt;TListElems&gt;... 位似乎会违反此说明,但这并不完全清楚。

问题是:谁是对的? GCC、Clang 还是其他?

【问题讨论】:

    标签: c++ c++11 templates language-lawyer variadic-templates


    【解决方案1】:

    我认为 clang 就在这里。

    void (*)(std::list&lt;TListElems&gt;..., std::vector&lt;TVectorElems&gt;...) 中,TListElems...a non-deduced context,这使得TVectorElems... 也是a non-deduced context。但是两个参数包可以从两个元组指针参数中推导出来,并且预计也可以在这里use that deduction result

    我提交了gcc bug 83542

    【讨论】:

    • §17.9.2.5/6 提供了 Jarod42 的解决方案有效的原因(特别是:“如果将类型指定为 void f(typename A&lt;T&gt;​::​B, A&lt;T&gt;),则 A&lt;T&gt;​::​B 中的 T 是非推断的,但 @ A&lt;T&gt; 中的 987654331@ 是推导出来的”),但对于 Clang 或 GCC 是否真的有正确的解释,我感到模棱两可。
    【解决方案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&lt;void (std::list&lt;TListElems&gt;...,std::vector&lt;TVectorElems&gt;...)&gt;*
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多