C++标准容器-没有指针的多态性.有可能吗?

nszi6y05  于 2023-03-14  发布在  其他
关注(0)|答案(5)|浏览(168)

有没有可能在 * 任何 * std c++容器中保持派生类的知识而不使用指针,动态地从容器中转换返回值?我知道我可以创建一个向量,比如说,一些基类类型的指针,并让它们保留它们的子类。但问题是我必须使用指针吗?
示例:

struct A {
  int x = 0, y = 0, z = 0;
  virtual void foo() { cout << "A" << endl; };
};

struct B : public A {
  int a = 1, b = 1, c = 1;
  virtual void foo() { cout << "B" << endl; };
};

int main() {  
  <SOMECONTAINER><A> a(2);
  a[0] = A();
  a[1] = B();
  B * p;
  B& n = dynamic_cast<B&>(a[1]); // Always throws?
  p = dynamic_cast<B*>(&a[1]); // Always zero?
  cout << p << endl;
}
nfzehxib

nfzehxib1#

是的,你必须使用指针,否则,试图把一个B放入A的容器中会导致slicingB被削减为A(这不限于容器,如果您执行A a = B()或将B传递给期望A的函数,则会发生完全相同的情况)。
当你后来把它拿出来时,它是一个A,完全不知道它的血统包括B类型的杰出祖先--不管你怎么看A,你都不能把它变成B

ctzwtxfj

ctzwtxfj2#

我将忽略对齐,或者更确切地说,假设指针后面的数据充分对齐。

template<class T, unsigned N>
struct poly_anna;
template<class T,unsigned N>
struct poly_bob {
  typedef poly_anna<T,N> poly_anna_;
  T*(*get)(poly_anna_*) = nullptr;
  void(*destroy)(poly_anna_*) = nullptr;
  void(*move_to)(poly_anna_ *,poly_anna_*) = nullptr;
  void(*copy_to)(poly_anna_ const*, poly_anna_*)=nullptr;
};

template<class T, unsigned N>
struct poly_anna {
private:
  poly_bob<T,N> const*bob=nullptr;
  char buff[N];
public:
  template<class U> static poly_bob<T,N> const* get_bob() {
    static poly_bob<T,N> b={
      [](poly_anna*a)->T&{ return *(U*)&a->buff[0]; },
      [](poly_anna*a){ ((U*)&a->buff[0])->~U(); a->bob = nullptr; },
      [](poly_anna*s,poly_anna*d){
        if (s->bob==d->bob){
          *((U*)&d->buff[0])=std::move(*((U*)&d->buff[0]));
          return;
        }
        if (d->bob != nullptr) {
          d->bob->destroy(b);
        }
        d->store( std::move( *(U*)&s->buff[0] ) );
      },
      [](poly_anna const* s, poly_anna*d){
        if (d->bob == s->bob){
          *(U*)&d->buff[0] = *(U const*)&s->buff[0];
          return;
        }
        if (d->bob){ d->bob->destroy(d); }
        d->store( *(U const*)*s->buff[0] );
      }
    };
    return &b;
  };
  template<class U_>
  void store(U_&& u){
    typedef typename std::decay<U_>::type U;
    static_assert( sizeof(U)<=N, "N not large enough" );
    if (bob) bob->destroy( this );
    bob = get_bob<U>();
    new (&buff[0]) U( std::forward<U_>(u) );
  }
  void reset(){ if (bob) bob->destroy(this); }
  T& get() {
    return bob->get(this);
   }
  T const& get() const {
    return bob->get(const_cast<poly_anna*>(this));
   }
   poly_anna( poly_anna const& o ){
     if (o.bob) o.bob->copy_to( &o, this );
   }
   poly_anna( poly_anna && o ){
     if (o.bob) o.bob->move_to( &o, this );
   }
   poly_anna&operator=( poly_anna const& o ){
     if (o.bob) o.bob->copy_to( &o, this );
     else if (bob) bob->destroy(this);
     return *this
   }
   poly_anna&operator=( poly_anna && o ){
     if (o.bob) o.bob->move_to( &o, this );
     else if (bob) bob->destroy(this);
     return *this
   }
   poly_anna()=default;
   ~poly_anna(){if(bob)bob->destroy(this);}
   explicit operator bool()const{return bob;}
};

这是我对多态变体的尝试,它存储TT的孩子,只要他们不大于N,并且可以存储在std容器中。
编译好了告诉我。

rfbsl7qr

rfbsl7qr3#

你需要指针用于虚成员函数调度。
当你考虑它的时候,如果没有指针,你就只能“按值”了,值语义和多态性在一起并没有什么意义。
一个 value 只有一个上下文/类型。它是原子的,简单的。可以说是所见即所得。当然,你可以强制转换它,但是那样你就有了...另一个值。

u1ehiz5o

u1ehiz5o4#

有一句经常被引用的程序员谚语:除了太多的间接层之外,没有任何问题不能用附加的间接层来解决。
将其付诸实践,请看boost::variant
如果你的容器存储了boost::variant,允许所有你想要存储的(子)类,你可以避免指针。
这可能是一场胜利,但也不一定是。
在你承诺这样一个解决方案之前进行衡量。

ldioqlga

ldioqlga5#

我认为这里实际上有两个问题:
1.有没有可能在STL容器中获得多态语义而不使用指针和相关的动态分配?
1.有没有可能保留存储在STL容器中的多态对象的具体类型?
1的答案是肯定的,但要付出一些努力。正如一些人提到的,一种方法是使用boost::variant这样的变体类型。这种方法的问题是,您失去了与存储在变体中的对象自然交互的能力,而是必须编写访问者,这会产生大量的语法开销。
如果类的层次结构是有界的,那么一个更好的方法可能是使用boost::variant的一个变体(没有双关语),这个变体是专门设计来保留多态语义和相关语法的,emplacer就是一个例子,正如dyp上面的注解所指出的,emplacer是一个受限的变体。
至于问题2,我不知道有没有不使用typeid()或手动滚动类型系统的方法。

相关问题