使用C++17,我需要修改下面的
struct Bind
来处理具有任意参数的方法。方法的返回值始终为 void
。目标是在 constexpr 上下文中创建函数指针,该指针可用于稍后在非 constexpr 上下文中创建 std::functions
。
https://godbolt.org/z/q4M1KxqPh
#include <functional>
template <typename Owner, typename Owned, Owned Owner::* Member, void(Owned::* Method)(int)>
struct Bind {
static auto apply(Owner* owner) {
return std::function<void(int)>( [=](int x) { ((owner->*Member).*Method)(x); } );
}
};
struct Foo {
void x(int x) { }
void y(float y, int x) { }
};
struct Bar {
Foo foo1;
Foo foo2;
};
int main() {
Bar bar;
std::function<void(int)> (*bindX)(Bar* owner) = &Bind<Bar, Foo, &Bar::foo1, &Foo::x>::apply;
std::function<void(int)> targetX = bindX(&bar);
targetX(42);
std::function<void(float, int)> (*bindY)(Bar* owner) = &Bind<Bar, Foo, &Bar::foo2, &Foo::y>::apply;
std::function<void(float, int)> targetY = bindY(&bar);
targetY(3.14f, 42);
}
error: could not convert template argument '&Foo::y' from 'void (Foo::*)(float, int)' to 'void (Foo::*)(int)'
上面演示了两个示例参数列表。但是,Bind 需要处理具有在编译时固定的任意参数的函数签名。
这个问题看起来需要类似的东西
template <typename Owner, typename Owned, Owned Owner::* Member, typename ...Args, void(Owned::* Method)(Args...)>
struct Bind {
static auto apply(Owner* owner) {
return std::function<void(int)>( [=](Args&&... args) {
((owner->*Member).*Method)(std::forward<Args>(args)...);
});
}
};
但是,这行不通,因为
error: parameter pack 'Args' must be at the end of the template parameter list
或者,也许
template <typename Owner, typename Owned, Owned Owner::* Member, template<typename ...Args> void(Owned::* Method)(Args...)>
struct Bind {
static auto apply(Owner* owner) {
return std::function<void(int)>( [=](Args&&... args) {
((owner->*Member).*Method)(std::forward<Args>(args)...);
});
}
};
但是,这也无法解析。
expected 'class' or 'typename' before 'void'
有“模板化模板非类型参数”这样的东西吗?
这看起来也对
template <auto Method>
很有用。但是,我不知道如何使 lambda 签名起作用......
灵感来自https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0127r1.html
我设法通过模板部分专业化解决了这个问题。 https://godbolt.org/z/7doP5563e
#include <functional>
template <typename Owner, typename Owned, Owned Owner::* Member, typename Method, Method method>
struct Bind;
template <typename Owner, typename Owned, Owned Owner::* Member, typename... Args, void(Owned::* method)(Args...)>
struct Bind<Owner, Owned, Member, void(Owned::*)(Args...), method> {
static auto apply(Owner* owner) {
return std::function<void(Args...)>( [=](Args&&... args) {
((owner->*Member).*method)(std::forward<Args>(args)...);
});
}
};
struct Foo {
void x(int x) { }
void y(float y, int x) { }
};
struct Bar {
Foo foo1;
Foo foo2;
};
int main() {
Bar bar;
std::function<void(int)> (*bindX)(Bar* owner) = &Bind<Bar, Foo, &Bar::foo1, decltype(&Foo::x), &Foo::x>::apply;
std::function<void(int)> targetX = bindX(&bar);
targetX(42);
std::function<void(float, int)> (*bindY)(Bar* owner) = &Bind<Bar, Foo, &Bar::foo2, decltype(&Foo::y), &Foo::y>::apply;
std::function<void(float, int)> targetY = bindY(&bar);
targetY(3.14f, 42);
}