我想允许类型指定为模板参数的可调用对象使用多个签名。更具体地说,我有一个模板化的update
方法,它接受一个必须返回float
的可调用对象,并使用它来更新数据网格中的值。对此的简化说明是
template <typename Fn>
void update(Fn&& fn_)
{
for (Vec3 pos : m_grid)
{
float val = fn_(pos, m_grid)
m_grid(pos) = val;
...
在上述情况下,fn_
的签名必须始终同时具有 pos 和网格作为参数,即使在实现中忽略它们也是如此
我想使用一些模板魔术来允许回调签名的多种排列。
特别是,我想允许接受pos
但不grid
的回调,以及根本不接受参数的回调。 我不介意是否强制执行参数的排序。
有人对如何做到这一点有任何提示吗?我不介意使用 boost 或其他库,但它们应该是仅标题的。
您可以使用 SFINAE 使用 is_invocable
的辅助函数来执行此操作(C++17:std::is_invocable
,或更早的 boost: boost::callable_traits::is_invocable
)
template <typename Fn,
std::enable_if_t<std::is_invocable<Fn, Vec3, Grid>::value>* = nullptr>
float call_helper(Fn&& fn_, const Vec3& pos_, const Grid& grid_)
{
return fn_(pos_, grid_);
}
template <typename Fn,
std::enable_if_t<std::is_invocable<Fn, Vec3>::value>* = nullptr>
float call_helper(Fn&& fn_, const Vec3& pos_, const Grid& grid_)
{
return fn_(pos_);
}
template <typename Fn,
std::enable_if_t<std::is_invocable<Fn, Grid>::value>* = nullptr>
float call_helper(Fn&& fn_, const Vec3& pos_, const Grid& grid_)
{
return fn_(grid_);
}
template <typename Fn>
void update(Fn&& fn_)
{
for (Vec3 pos : m_grid)
{
float val = call_helper(fn_, pos, m_grid)
m_grid(pos) = val;
...
特别是,我想允许采用 pos 但不采用网格的回调,以及根本不采用参数的回调。
只需定义两个重载并使用 lambda 通过将请求转发到完整函数来执行此操作,从而过滤额外的参数。
举一个最小的工作示例:
struct S {
template <typename Fn>
auto update(Fn &&fn_)
-> decltype(fn_(0, 0), void())
{
// ...
fn_(0, 0);
// ...
}
template <typename Fn>
auto update(Fn &&fn_)
-> decltype(fn_(0), void())
{ update([&fn_](auto a, auto) { fn_(a); }); }
template <typename Fn>
auto update(Fn &&fn_)
-> decltype(fn_(), void())
{ update([&fn_](auto, auto) { fn_(); }); }
};
int main() {
S s;
s.update([](auto, auto) {});
s.update([](auto) {});
s.update([]() {});
}