参数归纳与标准::变体
Parameter induction with std::variant
最近我正在研究一个ORM,它通过执行以下操作来接受函数注册:
orm->register_func("NAME", &User::set_name);
所以基本上当数据库返回列NAME
时,ORM会使用User
中的函数set_name
。在开发过程中,我学到了更多关于std::variant
的知识,这里有一个小例子来说明我如何使用它:
template<typename RET, typename T, typename ...Args>
using FPTR = RET(T::*)(Args...);
template<typename T>
using VARIANT_FPTR = std::variant<FPTR<void, T, const char *>, FPTR<void, T, const unsigned int>, long int>;
FPTR
基本上是一个泛型函数指针(假设该函数是一个类成员(,VARIANT_FPTR
定义了我可以在ORM中注册的函数的可能性。
现在,由于std::variant
类型,我遇到了问题。以下是register_func
实现:
void register_func(std::string name, VARIANT_FPTR<T> ptr)
{
_map.insert(std::make_pair(name, ptr));
}
但是,我收到编译错误,因为像&User::set_name
这样的参数不是std::variant
类型。
有没有办法让register_func
诱导其参数的类型? 如果没有,有没有另一种方法可以进行类似的过程?
编译错误为:
error: cannot convert ‘<unresolved overloaded function type>’ to ‘VARIANT_FPTR<services::User>’ {aka ‘std::variant<void (services::User::*)(const char*), void (services::User::*)(unsigned int), long int>’}
下面是映射定义:
template<typename T> class Orm
{
private:
std::map<std::string, VARIANT_FPTR<T>> _map;
// ...
};
您需要在std::map
模板的实例化中提供确切的类型。例如,如果需要User
的成员函数,则需要以下内容:
std::map<std::string, VARIANT_FPTR<User>> _map;
// ^^^^^
下面是一个完整的最小工作示例:在线观看直播
#include <iostream>
#include <variant>
#include <string>
#include <map>
struct User
{
template<typename T>
void set_name(T)
{}
};
template<typename RET, typename T, typename ...Args>
using FPTR = RET(T::*)(Args...);
template<typename T>
using VARIANT_FPTR = std::variant<FPTR<void, T, const char*>, FPTR<void, T, const unsigned int>, long int>;
template<typename T> class Orm
{
private:
std::map<std::string, VARIANT_FPTR<T>> _map;
public:
void register_func(std::string name, VARIANT_FPTR<User> ptr)
{
_map.emplace(name, ptr);
}
};
int main()
{
Orm<User> orm{};
orm.register_func("NAME", &User::set_name<const char*>);
orm.register_func("NAME", &User::set_name<const unsigned int>);
return 0;
}
问题(假设编译器正确说所讨论的成员函数已重载(是std::variant<X,Y>
没有构造函数接受X
和Y
。相反,它有一个构造函数模板,该模板执行重载解析以确定要构造的替代项。 该模板无法推断从指针(或指向成员的指针(到重载集的未知成员的简单T
。 如果只有一个重载适合任何变体的选择,您可以自己提供这些重载:
template<class T>
auto pick(FPTR<void, T, const char *> f) {return f;}
auto pick(FPTR<void, T, const unsigned int> f) {return f;}
/* elsewhere */ {
orm->register_func("NAME", pick(&User::set_name));
}
如果这仍然不明确,您可以使用static_cast
来选择重载:
orm->register_func
("NAME",
static_cast<FPTR<void, T, const char *>>
(&User::set_name));
显然,在这种棘手的情况下,使用 std::variant 进行参数归纳并不总是可行的。因此,为了解决这个问题,我最终决定使寄存器函数的使用更加冗长。在这里:
template<typename RES, typename CLASS, typename ...Args>
void register_func(std::string name, FPTR<RES, CLASS, Args...> ptr) {
_map.insert(std::make_pair(name, ptr));
}
因此,要使用它,您需要编写:
orm->register_func<void, Class, const char *>("name", &Class::set_name);
有趣的事实
我发现在某些情况下,某些参数实际上可以归纳。在以下函数的情况下:
void Class::set_id(unsigned long id)
我显然可以这样称呼register_function:
orm->register_func("id", &Class::set_id);
- std::具有相同基类的类的变体
- 访问者访问变体并返回不同类型时出错
- 我应该使用什么来代替void作为变体中的替代类型之一
- 将shared_ptr移动到<StructA>shared_ptr<变体<结构A、结构 B>>
- 如何比较自定义类的std::变体
- 通过网络、跨平台传递std::变体是否安全
- 对于循环变体比较
- 当无法使用模板和宏时,生成类型变体C++代码的最简单方法是什么?
- 标准::变体的赋值运算符
- 如何@ref同一方法的不同变体?
- 无限嵌套具有变体的地图
- Android Studio:如何在build.gradle中定义自定义宏(针对不同的构建变体),并让原生C / C++
- std::带有前向声明的变体
- 标准::C++中的变体
- 参数归纳与标准::变体
- 如何添加到标准::变体?
- 如何流式传输标准::变体<...,...>
- 标准中变体成员的定义
- 什么是C++标准库中boost::变体的等价物
- 最佳实践:boost::变体和标准容器