将对象转换为其基数的子集

Casting an object to a subset of of its bases

我有一个class定义如下:

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

问题是:

编辑: 让我在没有模板的情况下改写这个问题。 我有一个 class 定义如下:

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

问题是:

编辑 2: 进一步澄清: 我正在实现一个通用的访问者模式实现,我将在一个通用的树容器中使用它。我正在使用 mixin (CRTP) 模式来实现已访问 classes 的 accept 方法以及访问者的 visit 方法。这些方法是虚拟的,我需要我在 OP 中发布的层次结构。 有一个名为 IVisitor<Ts...> 的基 class 实现了访问 Ts... 的访问者的接口。这个界面由 classes 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... 的排列,那将会很有用。为了做到这一点,我需要将它投射到那个基地 - 因此问题。

我不太确定这是否是您要找的,但就是这样。 我会将基础 类 的 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 方法采用访问者的基 classes 之一。这样您就可以将任何访问者传递给 accept 方法,因为所有访问者都可以转换为单个基本访问者 class。我相信这对你有用:

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


}

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