将对象强制转换为其基的子集

Casting an object to a subset of of its bases

本文关键字:子集 转换 对象      更新时间:2023-10-16

我有一个定义如下的类:

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...的子集或排列,那将很有用。为了做到这一点,我需要把它投射到那个基础上——因此问题来了。

在进一步检查了这个问题之后,我意识到我需要一个包装器来适应我的接口的通用访问者。这是我使用的解决方案 - 请参阅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;
}

我不太确定这是否是您要找的,但在这里。我会将基类的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
}

这样,您就不再需要对访客类进行任何转换。

我认为您不需要使用排列参数投射给访问者来完成您想要的,您唯一需要的是额外的抽象级别 - 可变参数模板包装器给您的访问者。找到下面的代码:

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);
}

另一个想法是让 accept 方法采用访问者的基类之一。这样,您可以将任何访问者传递给 accept 方法,因为所有访问者都可以强制转换为单个基 visitor 类。我相信这对你有用:

#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);

}