如何实现静态虚成员函数

How to implement static virtual member function

本文关键字:静态 成员 函数 实现 何实现      更新时间:2023-10-16

TL/DR:有没有(更方便的)方法来实现这样的功能?

我必须为类类型和类实例调用相同的静态代码,它们由对base的引用表示:

int main()
{
    // Invokes on class
    bar<C1>(); // invokes C1::foo()
    bar<C2>(); // invokes C2::foo()
    // Invokes on instance
    bar(C1()); // invokes C1::foo()
    bar(C2()); // invokes C2::foo()
}

但问题是,如果没有一些代码复制,我就无法实现它。对于每个派生类,我必须同时编写静态和虚拟方法。静态——因为我不能在类上调用虚方法,而虚方法调用静态——因为除了虚方法,没有办法区分对象的行为:

template<typename T>
void bar()
{
    T::foo();
}
void bar(A const &r)
{
    r.foo();
}

因此,我解决重复代码问题的方法是:

#include <iostream>
class A
{
public:
    virtual void foo() const = 0;
};
template<typename derived_T>
class B: public A
{
public:
    virtual void foo() const
    {
        derived_T::foo();
    }
};
class C1 : public B<C1>
{
public:
    static void foo()
    {
        std::cout << "C1::foo()" << std::endl;
    }
};
class C2 : public B<C2>
{
public:
    static void foo()
    {
        std::cout << "C2::foo()" << std::endl;
    }
};

这种方法工作得很好,但是它至少有两个不便之处。

首先,我必须引入一个辅助模板类B,并实现虚方法。

其次,每个继承链(从B到final类)必须由模板组成,这使得不可能使用任何中间类作为指针/引用类型。如。A <- B <- T1 <- T2 <- C、T1和T2必须是模板类,以提供C::foo()。

一个可能的解决方案是使用基类注入的CRTP。

template <typename T, typename... Bases> 
struct CRTPFooInjector : Bases... 
{
    virtual void vfoo() { T::foo(); }
};    

这是你的注入器模板。它只实现了foo的虚版本,没有其他的。

struct Base: CRTPFooInjector<Base>
{
    static int foo() { std::cout << "Base::foo()" << std::endl; }
};

struct Der1 : CRTPFooInjector<Der1, Base>
{
    static int foo() { std::cout << "Der1::foo()" << std::endl; }
};

struct Der2 : CRTPFooInjector<Der2, Base>
{
    static int foo() { std::cout << "Der2::foo()" << std::endl; }
};

struct Der12 : CRTPFooInjector<Der12, Der1, Der2>
{
    static int foo() { std::cout << "Der12::foo()" << std::endl; }
};

现在你有一个稍微不同的Injector <- Base <- Injector <- Der1 <- Injector <- Der12,而不是你正常的层次结构Base <- Der1 <- Der12,但这对大多数用户来说应该是透明的。

如果你需要混合一些虚拟基类:

template <typename T>
struct Virtual : virtual T
{
};
struct Der1 : CRTPFooInjector<Der1, Virtual<Base>> ...

如果你有几个静态方法,你的派生类是固定的,你可以使用访问者模式:

class IAVisitor;
struct A
{
    virtual ~A() = default;
    virtual void accept(IAVisitor& visitor) = 0;
};
struct C1; struct C2; /*..*/; struct Cn;
struct IAVisitor
{
    virtual ~IAVisitor() = default;
    virtual visit(C1&) = 0;
    virtual visit(C2&) = 0;
    //...
    virtual visit(Cn&) = 0;
};
struct C1 : A
{
    void accept(IAVisitor& visitor) override {visitor.visit(*this);};
    static void foo();
};
// Similar code for C2, Cn
template <typename F>
struct AVisitor_F : IAVisitor
{
    AVisitor_F(F f) : f(f) {}
    void visit(C1& a) override { f(a); }
    void visit(C2& a) override { f(a); }
    //...
    void visit(Cn& a) override { f(a); }
    F f;
};

struct FooCaller
{
    template<typename T>
    void operator () (const T&) const
    {
        T::foo();
    }
};

最后:

AVisitor_F<FooCaller> fooVisitor;
a.accept(fooVisitor);