std::variant参数归纳
Parameter induction with std::variant
最近我正在开发一个 ORM,它通过执行以下操作接受函数注册:
orm->register_func("NAME", &User::set_name);
所以基本上当数据库 returns 列 NAME
时, ORM 将使用函数 set_name
in User
.在开发过程中,我了解了更多关于 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
基本上是一个 通用函数指针 (假设函数是 class 成员)并且 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;
// ^^^^^
以下是一个完整的最小工作示例:See live online
#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 进行参数归纳并不总是可行的。所以为了解决这个问题,我最终决定让 register 函数的使用更冗长一点。这是:
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::variant<X,Y>
没有 有构造函数接受 X
和 Y
。相反,它有一个构造函数 template 执行重载决策以确定要构造的替代方案。该模板无法从指向重载集未知成员的指针(或指向成员的指针)推断出简单的 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));
最近我正在开发一个 ORM,它通过执行以下操作接受函数注册:
orm->register_func("NAME", &User::set_name);
所以基本上当数据库 returns 列 NAME
时, ORM 将使用函数 set_name
in User
.在开发过程中,我了解了更多关于 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
基本上是一个 通用函数指针 (假设函数是 class 成员)并且 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;
// ^^^^^
以下是一个完整的最小工作示例:See live online
#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 进行参数归纳并不总是可行的。所以为了解决这个问题,我最终决定让 register 函数的使用更冗长一点。这是:
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::variant<X,Y>
没有 有构造函数接受 X
和 Y
。相反,它有一个构造函数 template 执行重载决策以确定要构造的替代方案。该模板无法从指向重载集未知成员的指针(或指向成员的指针)推断出简单的 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));