c++ - 为什么 std::function 不适用于函数模板?
问题描述
我开始使用模板,并试图弄清楚为什么以下内容不起作用。
class testclass1
{
public:
template <typename...Ts>
using TFunc = std::function<void(Ts*...)>;
template <typename...Ts>
void SetFunction(TFunc<Ts...> tf)
{
// Do something
}
};
template<typename...Ts>
class testclass2
{
public:
using TFunc = std::function<void(Ts*...)>;
void SetFunction(TFunc tf)
{
// Do something
}
};
void some_function(std::string*, double*)
{
}
int main()
{
testclass1 tc1;
testclass2<std::string, double> tc2;
testclass1::TFunc<std::string, double> tf1 = some_function;
tc1.SetFunction<std::string, double>(tf1);
tc1.SetFunction<std::string, double>(testclass1::TFunc<std::string, double>{tf1});
tc2.SetFunction(some_function);
tc1.SetFunction<std::string, double>(some_function);
}
一切正常,除了最后一个版本
tc1.SetFunction<std::string, double>(some_function);
,这实际上是我想做的,因为它的样板要少得多,而且我不希望 testclass1 需要模板参数。
最后一行给出以下编译错误:
调用“SetFunction”没有匹配的成员函数
- 候选模板被忽略:无法匹配 'function<void (std::__1::basic_string *, double *, type-parameter-0-0 ...)>' 与 'void ( )(std::__1::basic_string *, 双倍的 *)'
我不明白额外的“type-parameter-0-0”是什么。直观地说,这看起来很像链接到隐式 *this 指针的问题,但我不明白为什么 TFunc 会成为成员函数并具有隐式 *this 指针(或者 this 是否链接到 std::function?)。此外,将代码移到template <typename...Ts> using TFunc = std::function<void(Ts*...)>;
testclass1 之外并不会改变任何内容。
通过浏览不同的相关问答,您似乎不能将 std::function 与模板函数一起使用。这是这里的问题吗?
有没有办法tc1.SetFunction<std::string, double>(some_function);
在 testclass1 中工作?
提前致谢。
更新:我正在尝试解释解释,我想我有点明白了,但是——考虑一下——我仍然不是 100% 确定。考虑以下代码:
template <typename...Ts>
using test_tuple = std::tuple<Ts...>;
template <typename...Ts>
void test_func(test_tuple<Ts...> tup)
{
// Do something
// e.g., PrintTuple(tup);
}
int main()
{
test_tuple<std::string, double> tt{"Hi", 3.1459f};
test_func<std::string>(tt);
}
在这种情况下,仅std::string
在参数包中指定并推导出双精度(因此并非所有参数都在包中指定)。但是为什么它在这种情况下有效并且我没有收到错误消息?
解决方案
似乎您不能
std::function
与模板函数一起使用
不,这个问题与std::function
.
问题是模板参数包的扣法失败。通常,当您显式指定所有模板参数时,不会执行任何推导。
tc1.SetFunction<std::string, double>(some_function); // no deduction needed, right?
但在打包的情况下,总是会进行扣除。这是因为可以指定一些包参数并让编译器推断其余的,并且无法知道您是否只指定了部分或全部参数。
但std::function<void(std::string*, double*)>
不能推算void(*)(std::string*, double*)
(推算时不考虑用户定义的转换)。因此错误:
template argument deduction/substitution failed:
mismatched types 'std::function<void(Ts* ...)>' and 'void (*)(std::string*, double*)'
(注意 - GCC 11 输出中没有提及“ type-parameter-0-0 ” ,因此您的编译器可能已过时)。
现在,为了解决这个问题,我们可以为 的参数引入一个非推导上下文SetFunction
。然后将从提供的类型参数执行替换。
例如使用身份模板:
template<typename T>
struct type_identity {
using type = T;
};
class testclass1
{
public:
template <typename...Ts>
using TFunc = std::function<void(Ts*...)>;
template <typename...Ts>
void SetFunction(typename type_identity<TFunc<Ts...>>::type tf)
{
// Do something
}
};
(现场演示)
关于更新和后续问题 -test_tuple
参数很高兴从test_tuple
参数推导出 - 不涉及转换,它只是模式匹配。Butstd::function
是一个类,它可以由函数指针构造,但它们是不同的类型,所以不能从另一个推导出来。
推荐阅读
- javascript - 计算一个坐标是否在一串坐标中
- django - 如何在单击时渲染属于父模型的特定对象的子模型的所有对象?
- laravel - 使用 Laravel 作为 N 层架构
- python - 将背景颜色限制为 QTabBar 上的三角形选项卡
- css - 按钮未与 div 中的图像对齐
- javascript - 如何使用 JavaScript 存储文本值
- python - Django - 如何让电子邮件链接在移动设备上工作?
- sql - 数据库何时锁定数据?
- node.js - Firestore 查询 where(documentId(), 'in', array) 给我一个错误
- c++ - 标头结构中的 DNS 查询格式 little-big endian 问题