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

时间:2016-07-08 15:40:40

标签: c++ casting multiple-inheritance template-meta-programming

我的课程定义如下:

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...的排列的内容,那将会非常有用。为了做到这一点,我需要将它投射到那个基础 - 因此问题。

4 个答案:

答案 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);


}