为什么模板参数解包有时不适用于 std::function?

Wan*_*ang 6 c++ templates variadic-templates std-function c++17

我遇到了一个问题。当我使用类似的东西时,std::function<A(Fs...)>它不起作用,但std::function<A(Fs..., B)>确实起作用。这是在 Clang 8.0 下;它在 GCC 下都不起作用。这是示例:

#include <functional>
template<typename A, typename B, typename ...Fs>
void func_tmpl1(std::function<A(Fs..., B)> callable)
{
}
template<typename A, typename ...Fs>
void func_tmpl2(std::function<A(Fs...)> callable)
{
}
class Cls1{};
void func0(std::function<void(float, Cls1)> callable)
{

}

int main()
{
    std::function<void(float, Cls1)> f1 = [](float a, Cls1 b){};
    func0(f1);
    func0([](float a, Cls1 b){});
    func_tmpl1<void, Cls1, float>(f1); // fails in GCC
    func_tmpl2<void, float, Cls1>(f1);

    func_tmpl1<void, Cls1, float>( // fails in GCC
        [](float a, Cls1 b)
        {

        }
    );
    func_tmpl2<void, float, Cls1>( // fails in both
        [](float a, Cls1 b)
        {}
    );

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Godbolt 上,我们可以看到 GCC 总是失败,但 Clang 只在最后一次函数调用时失败。谁能解释这里发生了什么?

xsk*_*xzr 2

为了方便起见,我们将代码中的三个失败调用称为#1、#2 和#3。

问题是,当显式指定模板参数包对应的模板实参时,模板参数包是否仍然参与模板实参推导,如果参与,推导失败是否会导致整个调用格式错误?

来自[temp.arg.explicit]/9

模板参数推导可以扩展与模板参数包对应的模板参数序列,即使该序列包含显式指定的模板参数也是如此。

我们可以推断模板参数推导仍然应该执行。

在 的声明中func_tmpl1std::function<A(Fs..., B)>是一个非推导上下文([temp.deduct.type]/9:“如果 P 的模板参数列表包含不是最后一个模板参数的包扩展,则整个模板参数列表是一个非推导上下文) -推导的上下文。”),因此模板参数推导 forFs应被忽略,并且 #1 和 #2 都是格式良好的。有一个GCC 错误报告

对于#3,模板参数推导显然失败了(std::function<A(Fs...)>相对于 lambda 类型),但是推导失败真的会导致代码格式错误吗?在我看来,标准对此并不清楚,并且存在一个相关问题。从CWG 的回复来看,#3 确实格式不正确。