重载的模板类

Overloaded Template Classes

本文关键字:重载      更新时间:2023-10-16

>我正在用 c++ 开发一个类似属性的系统,其中一个组件在访问器模板类中,它创建静态函数,然后我可以将指针传递到我的属性类中以获取集合操作。

下面是访问器的外观:

template <typename T, typename V, V (T::*getf)(), void (T::*setf)(V)>
struct Accessor
{
    static V Get(T* obj)
    {
        return (obj->*getf)();
    }
    static void Set(T* obj, V aValue)
    {
        return (obj->*setf)(aValue);
    }
};

以及我将在其上使用访问器的测试类。注意 SetFoo 如何获取 int,其中 SetBar 采用 const int&。

class TargetClass
{
    int foo;
    int bar;
public:
    TargetClass(int f, int b)
        : foo(f)
        , bar(b)
    {
    }
    int GetFoo()
    {
        return foo;
    }
    void SetFoo(int f)
    {
        foo = f;
    }
    int GetBar()
    {
        return bar;
    }
    void SetBar(const int& b)
    {
        bar = b;
    }
};

最后,这是用法:

int main()
{
    TargetClass* target = new TargetClass(5, 3);
    // Works great
    typedef Accessor<TargetClass, int, &TargetClass::GetFoo, &TargetClass::SetFoo> fooAcessor;
    fooAcessor::Set(target, 13);
    int foo = fooAcessor::Get(target);
    // Doesn't work, because TargetClass::SetBar takes a const int& as an argument, instead of an int
    typedef Accessor<TargetClass, int, &TargetClass::GetBar, &TargetClass::SetBar> barAcessor;
    delete target;
    return 0;
}

我已经尝试"重载"Acessor 结构,所以它的第 4 个参数是常量 V&,但这似乎不起作用。我在这里想做的事情可能吗?

如果您可以访问 c++17,并且假设建议的标准在此期间没有更改,则可以自动推断出类型:

template <typename F>
struct get_types;
template <typename T, typename V, typename... VP>
struct get_types<V(T::*)(VP...)>
{
    using return_type = V;
    using class_type = T;
};
template <auto getf, auto setf>
struct Accessor {
    using V = typename get_types<decltype(getf)>::return_type;
    using T = typename get_types<decltype(getf)>::class_type;
    static V Get(T* obj)
    {
        return (obj->*getf)();
    }
    static void Set(T* obj, V aValue)
    {
        return (obj->*setf)(aValue);
    }
};
class TargetClass {
    int foo;
    int bar;
public:
    TargetClass(int f, int b)
        : foo(f)
        , bar(b) {}
    int GetFoo() { return foo; }
    void SetFoo(int f) { foo = f; }
    int GetBar() { return bar; }
    void SetBar(const int& b) { bar = b; }
};
int main()
{
    TargetClass* target = new TargetClass(5, 3);
    using fooAccessor = Accessor<&TargetClass::GetFoo, &TargetClass::SetFoo>;
    fooAccessor::Set(target, 13);
    int foo = fooAccessor::Get(target);
    using barAcessor = Accessor<&TargetClass::GetBar, &TargetClass::SetBar>;
    barAcessor::Set(target, 13);
    int bar = barAcessor::Get(target);
    delete target;
    return foo + bar;
}

撰写本文时,上面的程序在 gcc 7(快照)上编译。

您可以为 set 函数的参数添加另一个模板参数:

template <typename T, typename V, typename VP, V(T::*getf)(), void (T::*setf)(VP)>

然后用于两种情况:

typedef Accessor<TargetClass, int, int, 
    &TargetClass::GetFoo, &TargetClass::SetFoo> fooAcessor;
typedef Accessor<TargetClass, int, const int&, 
    &TargetClass::GetBar, &TargetClass::SetBar> barAcessor;

完整程序:

template <typename T, typename V, typename VP, V(T::*getf)(), void (T::*setf)(VP)>
struct Accessor
{
    static V Get(T* obj)
    {
        return (obj->*getf)();
    }
    static void Set(T* obj, V aValue)
    {
        return (obj->*setf)(aValue);
    }
};
class TargetClass
{
    int foo;
    int bar;
public:
    TargetClass(int f, int b)
        : foo(f)
        , bar(b)
    {
    }
    int GetFoo()
    {
        return foo;
    }
    void SetFoo(int f)
    {
        foo = f;
    }
    int GetBar()
    {
        return bar;
    }
    void SetBar(const int& b)
    {
        bar = b;
    }
};
int main()
{
    TargetClass* target = new TargetClass(5, 3);
    // now has extra int parameter
    typedef Accessor<TargetClass, int, int, &TargetClass::GetFoo, &TargetClass::SetFoo> fooAcessor;
    fooAcessor::Set(target, 13);
    int foo = fooAcessor::Get(target);
    // Works now, because TargetClass::SetBar's function parameter was specified explicitly as const int&
    typedef Accessor<TargetClass, int, const int&, &TargetClass::GetBar, &TargetClass::SetBar> barAcessor;
    barAcessor::Set(target, 13);
    int bar = barAcessor::Get(target);
    delete target;
    return 0;
}