如何实现静态虚成员函数
How to implement static virtual member function
TL/DR:有没有(更方便的)方法来实现这样的功能?
我必须为 class 类型和 class 实例调用相同的静态代码,由引用 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()
}
但问题是如果不重复一些代码我就无法实现它。对于每个派生的 class 我必须同时编写静态方法和虚拟方法。静态 - 因为我不能在 class 上调用虚拟方法,而虚拟调用静态 - 因为除了虚拟方法之外没有办法区分对象行为:
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;
}
};
这种方法非常有效,但至少有两个不便之处。
首先,我不得不引入一个辅助模板 class B 实现了虚拟方法。
其次,每个继承链(从 B 到最终 class)必须包含模板,这样就不可能使用任何中间 class 作为 pointer/reference 类型。例如。 A <- B <- T1 <- T2 <- C ,T1 和 T2 必须是模板 classes 才能提供 C::foo().
一种可能的解决方案是使用基础 class 注入的 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; }
};
现在您的普通层次结构 Base <- Der1 <- Der12
略有不同 Injector <- Base <- Injector <- Der1 <- Injector <- Der12
,但这对大多数用户来说应该是透明的。
如果你需要混入一些虚拟基地classes:
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);
TL/DR:有没有(更方便的)方法来实现这样的功能?
我必须为 class 类型和 class 实例调用相同的静态代码,由引用 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()
}
但问题是如果不重复一些代码我就无法实现它。对于每个派生的 class 我必须同时编写静态方法和虚拟方法。静态 - 因为我不能在 class 上调用虚拟方法,而虚拟调用静态 - 因为除了虚拟方法之外没有办法区分对象行为:
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;
}
};
这种方法非常有效,但至少有两个不便之处。
首先,我不得不引入一个辅助模板 class B 实现了虚拟方法。
其次,每个继承链(从 B 到最终 class)必须包含模板,这样就不可能使用任何中间 class 作为 pointer/reference 类型。例如。 A <- B <- T1 <- T2 <- C ,T1 和 T2 必须是模板 classes 才能提供 C::foo().
一种可能的解决方案是使用基础 class 注入的 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; }
};
现在您的普通层次结构 Base <- Der1 <- Der12
略有不同 Injector <- Base <- Injector <- Der1 <- Injector <- Der12
,但这对大多数用户来说应该是透明的。
如果你需要混入一些虚拟基地classes:
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);