我有一些 C++ 代码,它们使用模板来创建围绕成员函数的函数包装器。这是编译器资源管理器上代码的链接,下面是相同的代码。这是一些现实世界代码的简化,其中包装器更复杂。
模板
setter_fn
或 getter_fn
采用指向成员函数的指针,并生成带有签名 PrimImplFun
(又名 Obj* (*)(const vector<FunArg> &args)
)的顶级函数。 (这个动态类型签名是为脚本语言设计的。)
#include <iostream>
#include <vector>
#include <string>
using std::cout;
using std::vector;
using std::string;
struct Obj {};
struct FunArg {
string kw;
Obj *val;
};
using PrimImplFun = Obj*(*)(const vector<FunArg> &args);
struct Foo : public Obj {};
class Device : public Obj {
Foo *m_foo;
public:
Foo *foo() {
cout << "in foo()\n";
return m_foo;
}
Foo *get_foo() { return foo(); }
void foo(Foo *f) {
cout << "in foo(Foo*)\n";
m_foo = f;
}
void set_foo(Foo *f) { foo(f); }
};
// Wrapper templates
template<typename T> struct member_pointer_class;
template<typename Class, typename Value>
struct member_pointer_class<Value Class::*> {
using type = Class;
};
template<typename T> struct member_pointer_arg0;
template<typename Class, typename Value, typename A0>
struct member_pointer_arg0<Value (Class::*)(A0)> {
using type = A0;
};
template<typename>
constexpr bool is_one_arg_void_ret_member_function{};
template<typename Klass, typename A0>
constexpr bool is_one_arg_void_ret_member_function<void (Klass::*)(A0 a)> = true;
template <auto memfun>
Obj *getter_fn(const vector<FunArg> &args) {
using T = member_pointer_class<decltype(memfun)>::type;
T *obj = (T*)args[0].val;
auto res = (obj->*memfun)();
return res;
}
template <auto memfun>
requires is_one_arg_void_ret_member_function<decltype(memfun)>
Obj *setter_fn(const vector<FunArg> &args) {
using T = member_pointer_class<decltype(memfun)>::type;
using A0 = member_pointer_arg0<decltype(memfun)>::type;
T *obj = (T*)args[0].val;
A0 a0 = (A0)args[1].val;
(obj->*memfun)(a0);
return nullptr;
}
// End Wrapper templates
struct Prop {
string name;
PrimImplFun getter;
PrimImplFun setter;
};
Prop the_prop;
void make_prop(string name, PrimImplFun getter, PrimImplFun setter) {
the_prop = { name, getter, setter };
}
int main() {
// This line works. The templates create the top-level functions that wrap the member functions.
// make_prop("foo", getter_fn<&Device::get_foo>, setter_fn<&Device::set_foo>);
// This doesn't work, because `Device::foo` is overloaded. Is there a way to make the template pick
// the `foo(Foo*)` overload?
make_prop("foo", getter_fn<&Device::get_foo>, setter_fn<&Device::foo>);
Device dev;
Foo foo;
vector<FunArg> args = {{"", &dev}, {"", &foo}};
the_prop.setter(args);
the_prop.getter(args);
cout << "Done.\n";
}
如果我没有重载,它就可以工作,并且函数被称为
get_foo()
和 set_foo(Foo*)
,但我想知道当 getter 和 setter 成员函数是 foo()
时是否有办法让它工作
和 foo(Foo*)
。在这种情况下,来自 MSVC 的错误是(不是很有帮助):
1>C:\main.cc(95,5): error C2664: 'void make_prop(std::string,PrimImplFun,PrimImplFun)':
cannot convert argument 3
from 'Obj *(__cdecl *)(const std::vector<FunArg,std::allocator<FunArg>> &)'
to 'PrimImplFun'
1> C:\main.cc(95,51):
1> None of the functions with this name in scope match the target type
1> C:\main.cc(85,6):
1> see declaration of 'make_prop'
1> C:\main.cc(95,5):
1> while trying to match the argument list '(const char [4], overloaded-function, overloaded-function)'
我的目标是能够用一种表达方式创造包装乐趣,就像我对
setter_fn<&Class::memfun>
所做的那样。但我需要某种方法来告诉它选择哪个重载。有办法做到吗?
正如您在
setter_fn
模板上看到的,我尝试使用 requires
子句将其缩小为仅查看 foo(Foo*)
,而不是 foo()
,但这似乎不起作用。
是的,您可以通过显式指定要使用哪个重载来解决重载成员函数引起的歧义。在 C++ 中,当您有重载函数并且需要获取函数的地址时,必须将其转换为适当的函数指针类型以消除歧义。
调用
setter_fn
时,可以使用 static_cast
将 &Device::foo
强制转换为与所需重载对应的特定成员函数指针类型。这准确地告诉编译器您正在引用哪个函数。
按如下方式修改您的
make_prop
通话:
make_prop("foo",
getter_fn<&Device::get_foo>,
setter_fn<static_cast<void (Device::*)(Foo*)>(&Device::foo)>);