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

标签 c++ templates c++17 variadic-templates std-function

我遇到了一个问题。当我使用类似 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;
}

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

最佳答案

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

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

来自 [temp.arg.explicit]/9 :

Template argument deduction can extend the sequence of template arguments corresponding to a template parameter pack, even when the sequence contains explicitly specified template arguments.



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

func_tmpl1的声明中, std::function<A(Fs..., B)>是非推导上下文( [temp.deduct.type]/9 :“如果 P 的模板参数列表包含不是最后一个模板参数的包展开,则整个模板参数列表都是非推导上下文。”),因此模板参数推导为 Fs应该被忽略,#1 和 #2 都是格式良好的。有一个GCC bug report .

对于#3,模板参数推导显然失败(std::function<A(Fs...)> vs lambda 类型),但是推导失败真的会使代码格式错误吗?在我看来,这个标准不清楚,还有一个related issue .从 CWG 的回应来看,#3 确实格式错误。

关于c++ - 为什么模板参数解包有时不适用于 std::function?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61281556/

相关文章:

c++ - 定义模板函数的语法错误

c++ - 对于类型为类模板特化的参数,ADL 背后的基本原理是什么

c++ - 我们是否可以通过将#if-#else if和宏与参数结合起来,在C和C++中实现预处理程序级别的决策?

java - Java 不包括运算符重载(至少对于赋值而言)的更根本原因是什么?

c++ - std::string 和 std::wstring 的前向声明

c++ - 在 C++ 中创建一个特征来检测闭包类型

c++ - constexpr if 和 static_assert

c++ - 为什么我不能在constexpr lambda函数中使用std::tuple

c++ - 如何禁用特定符号的 Visual Studio 警告?

c++ - 使用两个不同大小的一维数组制作二维数组