我的课程定义如下:
template< typename... >
class Base;
template< typename T >
class Base<T>{
//pure virtual functions dependent on T
public:
virtual ~Base() = default;
};
template< typename... Ts >
class Base<Ts...>
: public Base<Ts>...
{
//empty
};
问题是:
Base< T1, T2, T3 >&
投放到Base< T2, T3 >&
? Base< T1, T2, T3 >&
投射到Base< T2, T1, T3 >&
?修改 让我在没有模板的情况下重新解释这个问题。 我的课程定义如下:
class Base_int{
public:
virtual int f() = 0;
virtual ~Base_int() = default;
};
class Base_float{
public:
virtual float f() = 0;
virtual ~Base_float() = default;
};
class Base_double{
public:
virtual double f() = 0;
virtual ~Base_double() = default;
};
class IFD
: public Base_int
, public Base_float
, public Base_double
{ };
class IF
: public Base_int
, public Base_float
{ };
class FID
: public Base_float
, public Base_int
, public Base_double
{ };
问题是:
IFD&
投放到IF&
? IFD&
投射到FID&
? 编辑2:
进一步澄清:
我将在通用树容器中使用通用访问者模式实现。我使用mixin(CRTP)模式来实现访问类的accept
方法以及访问者的visit
方法。这些方法是虚拟的,我需要在OP中发布的层次结构。
有一个名为IVisitor<Ts...>
的基类实现了访问Ts...
的访问者的界面。该接口由类IVisitor<T>
组成 - 就像在OP中一样。
实现IVisitor<Ts...>
的具体访问者定义如下:
class ConcreteVisitor : public IVisitor<T1, T2, T2, ...>{
//Implementation of the visit methods defined in IVisitor<Ts...>
virtual void visit( T1& v ) override { /*...*/ }
virtual void visit( T2& v ) override { /*...*/ }
virtual void visit( T3& v ) override { /*...*/ }
}
在我看来,如果ConcreteVisitor可以访问IVisitor<Us...>
中Us...
是子集或Ts...
的排列的内容,那将会非常有用。为了做到这一点,我需要将它投射到那个基础 - 因此问题。
答案 0 :(得分:1)
在仔细检查了问题之后,我意识到我需要一个能够将通用访问者适应我的界面的包装器。这是我使用的解决方案 - 请参阅IVisitable::adaptVisitor
静态方法:
#include <iostream>
#include <typeinfo>
#include <vector>
//Visitable base class
template< typename >
class Visitable;
//Visitable class interface
template< typename... >
class IVisitor;
//Assures that the visitor type derives from IVisitor
template< typename IVisitor_type >
class IVisitable
: public IVisitable<typename IVisitor_type::derives_from_IVisitor>
{ };
//Implements the Visitor adapter
template< typename Derived, typename Root, typename... Ts >
class VisitorWrapper;
//IVisitable base class
template< typename... Ts >
class IVisitable< IVisitor<Ts...> >{
public:
//Visitor interface type
using IVisitor = IVisitor<Ts...>;
//Visitor adapter type
template< typename F >
class VisitorAdapter;
//Helper function
template< typename F >
static VisitorAdapter<F&&> adaptVisitor( F&& f )
{ return { std::forward<F>(f) }; }
//Accept visitor pure virtual methods
virtual void accept( IVisitor& ) = 0;
virtual void accept( IVisitor& ) const = 0;
virtual void accept( IVisitor&& ) = 0;
virtual void accept( IVisitor&& ) const = 0;
protected:
virtual ~IVisitable() = default;
};
//Implements the visitor adapter of F class
template< typename... Ts >
template< typename F >
class IVisitable<IVisitor<Ts...>>::VisitorAdapter
: public VisitorWrapper< VisitorAdapter<F>, ::IVisitor<Ts...>, Ts... >
//Derives from VisitorWrapper that implements all of the virtual methods
{
public:
template< typename U >
void visit( U&& u ){ f( std::forward<U>(u) ); }
VisitorAdapter( F f_ ) : f(f_) { }
F f;
};
//Basic IVisitor of T
template< typename T >
class IVisitor<T>{
public:
using derives_from_IVisitor = IVisitor;
virtual void visit( T& ) = 0;
virtual void visit( const T& ) = 0;
virtual ~IVisitor() = default;
};
//IVisitor of Ts derives from Visitor<T>
template< typename T, typename... Ts >
struct IVisitor<T, Ts...>
: IVisitor<T>, IVisitor<Ts...>
{
using derives_from_IVisitor = IVisitor;
};
//Visitable base class. Final to prevent errors
template< typename Derived >
struct Visitable final
{
//Extends class wraps the Base class inheritance
template< typename Base >
class extends : public Base
{
public:
//Propagate the IVisitor interface.
using IVisitor = typename Base::IVisitor;
//Prevents incomprehensible errors when visitor doesn't
//define Derived as its visited class
static_assert(
std::is_base_of<::IVisitor<Derived>, IVisitor>::value
, "Base class visitor interface does not support visitation of this type"
);
//Implement accept method via CRTP
virtual void accept( IVisitor& v ) override{
static_cast< ::IVisitor<Derived>& >(v).visit( // Disambiguation
static_cast<Derived&>(*this) // CRTP
);
}
virtual void accept( IVisitor& v ) const override {
static_cast< ::IVisitor<Derived>& >(v).visit( // Disambiguation
static_cast<const Derived&>(*this) // CRTP
);
}
virtual void accept( IVisitor&& v ) override{
static_cast< ::IVisitor<Derived>&& >(v).visit( // Disambiguation
static_cast<Derived&>(*this) // CRTP
);
}
virtual void accept( IVisitor&& v ) const override{
static_cast< ::IVisitor<Derived>&& >(v).visit( // Disambiguation
static_cast<const Derived&>(*this) // CRTP
);
}
protected:
virtual ~extends() = default;
};
~Visitable() = delete;
};
template< typename > struct print_type;
//Uses CRTP to implement visit method of IVisitor
//Consumes the list of Ts... so that the inheritance hierarchy is linear
template<
typename Derived // CRTP
, typename... Rs // Base class (IVisitor<Rs...>)
, typename T // Currently implemented type
, typename... Ts // The rest of types
> class VisitorWrapper< Derived, IVisitor<Rs...>, T, Ts... >
: public VisitorWrapper< Derived, IVisitor<Rs...>, Ts... > //Consume T
{
//Cast to Derived and call visit method
virtual void visit( T& v ){ static_cast<Derived*>(this)->visit(v); }
virtual void visit( const T& v ){ static_cast<Derived*>(this)->visit(v); }
};
//Uses CRTP to implement visit method of IVisitor
template< typename Derived, typename... Rs, typename T >
class VisitorWrapper< Derived, IVisitor<Rs...>, T >
: public IVisitor<Rs...> //IVisitor<Ts...>
{
virtual void visit( T& v ){ static_cast<Derived*>(this)->visit(v); }
virtual void visit( const T& v ){ static_cast<Derived*>(this)->visit(v); }
};
/////////////////////////////////////////////////////
class ABCVisitor
: public IVisitor< class A, class B, class C >
{ };
class ABC : public IVisitable< ABCVisitor >
{};
class A : public Visitable<A>::extends<ABC> {};
class B : public Visitable<B>::extends<ABC> {};
class C : public Visitable<C>::extends<B> {};
int main()
{
auto p = [](const auto& v){ std::cout << typeid(v).name() << std::endl; };
auto printer = ABC::adaptVisitor( p );
A a; B b; C c;
std::vector< ABC* > vs{ &a, &b, &c };
for( const auto& v : vs )
v->accept(printer);
return 0;
}
答案 1 :(得分:0)
我不确定这是否是您正在寻找的东西,但在这里。
我会使基类的accept
方法模板化,以便只要访问者具有正确的概念,他们就可以接受任何访问者。特别是:
#include <iostream>
template <class Derived>
struct Base {
// templated visitor so that it can take any argument as long as it has the right concept
template <class Visitor>
void accept(Visitor& visitor) {
visitor.visit(static_cast<Derived&>(*this));
}
};
struct T1
: Base<T1> {};
struct T2
: Base<T2> {};
struct T3
: Base<T3> {};
struct Visitor1 {
void visit(T1& t1) {
std::cout << "Visiting T1 from Visitor1" << std::endl;
}
void visit(T2& t2) {
std::cout << "Visiting T2 from Visitor1" << std::endl;
}
};
struct Visitor2 {
void visit(T1& t1) {
std::cout << "Visiting T1 from Visitor2" << std::endl;
}
void visit(T2& t2) {
std::cout << "Visiting T2 from Visitor2" << std::endl;
}
void visit(T3& t3) {
std::cout << "Visiting T3 from Visitor2" << std::endl;
}
};
int main(int argc, const char * argv[]) {
Visitor1 visitor1;
Visitor2 visitor2;
T1 t1;
T2 t2;
T3 t3;
t1.accept(visitor1);
t1.accept(visitor2);
t2.accept(visitor1);
t2.accept(visitor2);
t3.accept(visitor2);
// t3.accept(visitor1); // triggers a compilation error as visitor1 cannot handle T3 types
}
这样您就不再需要对访问者类别进行任何演员表了。
答案 2 :(得分:0)
我认为您不需要使用置换参数转换给访问者来完成您想要的任务,您唯一需要的是额外的抽象级别 - 访问者的可变参数模板包装器。找到以下代码:
struct VisitorParent {
virtual ~VisitorParent() = default;
};
template <class T>
struct VisitorImpl: virtual VisitorParent {
virtual void visit(T &t) {
//...
}
};
template <class... Ts>
struct Visitor: virtual VisitorImpl<Ts>... { };
template <class T>
struct VisitorWrapperImpl {
VisitorParent *vp;
VisitorWrapperImpl(VisitorParent *vp): vp(vp) {}
void visit(T &t) {
VisitorImpl<T> *cvi = dynamic_cast<VisitorImpl<T> *>(vp);
if (cvi) {
cvi->visit(t);
}
}
};
template <class... Ts>
struct VisitorWrapper: VisitorWrapperImpl<Ts>... {
VisitorWrapper(VisitorParent *vp): VisitorWrapperImpl<Ts>(vp)... { }
template <class T>
void visit(T &t) {
VisitorWrapperImpl<T>::visit(t);
}
};
int main() {
Visitor<int, char, float> v;
VisitorWrapper<char,float> vw(&v);
char c;
vw.visit(c);
}
答案 3 :(得分:0)
另一个想法是让accept方法接受访问者的一个基类。这样,您可以将任何访问者传递给accept方法,因为所有访问者都可以转换为单个基本访问者类。我相信这对你有用:
#include <iostream>
// one of the base classes of your concrete visitors
template <class T>
struct VisitorUnit {
virtual void visit(T& t) = 0;
};
// the visitor interface which will be implemented by concrete visitors
template <class... T>
struct Visitor
: VisitorUnit<T>... {
};
// the base class of your leaf classes
template <class Derived>
struct Base {
void accept(VisitorUnit<Derived>& visitor) {
visitor.visit(static_cast<Derived&>(*this));
}
};
struct T1: Base<T1>{};
struct T2: Base<T2>{};
struct T3: Base<T3>{};
struct Visitor1
: Visitor<T1, T2, T3> {
void visit(T1& t) override {
std::cout << "T1 from Visitor1" << std::endl;
}
void visit(T2& t) override {
std::cout << "T2 from Visitor1" << std::endl;
}
void visit(T3& t) override {
std::cout << "T3 from Visitor1" << std::endl;
}
};
struct Visitor2
: Visitor<T3, T2> {
void visit(T2& t) override {
std::cout << "T2 from Visitor2" << std::endl;
}
void visit(T3& t) override {
std::cout << "T3 from Visitor2" << std::endl;
}
};
int main(int argc, const char * argv[]) {
T1 t1;
T2 t2;
T3 t3;
Visitor1 visitor1;
Visitor2 visitor2;
visitor1.visit(t1);
visitor1.visit(t2);
visitor1.visit(t3);
visitor2.visit(t2);
visitor2.visit(t3);
}