C ++继承和容器协方差 A B 容器A 容器B main.cpp 输出

问题描述

我正在尝试构建以下架构:

struct A{
   int x;
}
struct B: public A{
   int additinal_data;
}
struct ContainerA{
  std::vector<A> va; 
}
struct ContianerB{
  std::vector<B> vb;
  int additional_data;
}

我希望以一种不仅可以针对ContainerA类型而且可以针对ContainerB调用函数的方式来重组以下代码

double sum(ContainerA &ca) {
   double res = 0;
   for (const auto &a: ca.va) 
       res += a.x;
   return res;
}

有能力将类似的功能作为ContainerA的成员移动但也可以从ContainerB进行访问(即ContainerA ca; ca.sum()),这也很好。 但是,ContinerB与ContainerA不协变,因此我很难做到这一点。我该如何重整架构以允许进行此类调用

解决方法

sumContainerAContainerB一起使用的一种方法。

  1. 使功能成为功能模板。
  2. 更新ContainerAContainerB,使其可以在range-for循环以及标准库的<algorithm>标头中的许多函数中使用。
  3. li>
struct ContainerA
{
  std::vector<A> va; 

  // Add const and non-const versions of begin() and end().

  using iterator = std::vector<A>::iterator;
  using const_iterator = std::vector<A>::const_iterator;

  iterator begin() { return va.begin(); }
  iterator end() { return va.end(); }

  const_iterator begin() const { return va.begin(); }
  const_iterator end() const { return va.end(); }

};

struct ContianerB
{
  std::vector<B> vb;
  int additional_data;

  // Add const and non-const versions of begin() and end().

  using iterator = std::vector<B>::iterator;
  using const_iterator = std::vector<B>::const_iterator;

  iterator begin() { return vb.begin(); }
  iterator end() { return vb.end(); }

  const_iterator begin() const { return vb.begin(); }
  const_iterator end() const { return vb.end(); }

};

template <typename Container>
double sum(Container const& container)
{
   double res = 0;
   for (const auto &a: container) 
       res += a.x;
   return res;
}
,

如何表达ContainerB“扩展” ContainerA

目前还没有。您可以轻松地更改它:

struct ContainerA{
  std::vector<A> va; 
};
struct ContianerB{
  std::vector<B> va;
  int additional_data;
};

当名称匹配时,静态多态性最有效。

然后

template <typename Container>
double sum(Container const& container) 
/* requires AVector<Container> */
{
   double res = 0;
   for (const auto &a: container.va) 
       res += a.x;
   return res;
}

使用C ++ 20概念进行更好的验证

template<typename T> concept ARange = 
    std::range<T>
 && std::derived_from<T::value_type,A>;
,

我认为不可能通过pathname: this.props.history.location.pathname方法将值的派生引用到它的 Base 类型引用中,如隐式所示 Steve Jessop's covariance example ,因为那样您会创建一个“临时参考”,但是我可能是错的。因此,我改为使用一种方法(称为static_cast)返回可能强制转换的指针,该指针肯定会存在。

因为您可能真的想在getIdentifergetElement之间有明确的关系(如果代码重复),所以我写了一个基于ContainerA的示例,说明了如何实现它。否则,使用模板函数可能是可行的方法。

如果您使用的是结构,则不需要ContainerBclasspublic标签,也不需要变量的setter,但是您当然需要em>需要某种protected方法来访问private类型的元素。

这是一个演示程序:

A

getIdentifier

B

B

容器A

#pragma once
#include <iostream>

class A
{
public:
    A(const int& data);

    friend std::ostream& operator<<(std::ostream& s,const A& a);
protected:
    virtual std::ostream& toStream(std::ostream& s) const;
private:
    int m_data;
};

A::A(const int& data)
    : m_data{ data }
{
}

std::ostream& A::toStream(std::ostream& s) const
{
    return s << m_data;
}

std::ostream& operator<<(std::ostream& s,const A& a)
{
    return a.toStream(s);
}

容器B

#pragma once
#include "A.h"

class B : public A
{
public:
    B(const int& data,const int& additionalData);
    void setAdditionalData(int additionalData);
protected:
    virtual std::ostream& toStream(std::ostream& s) const;
private:
    int m_additinalData;
};

B::B(const int& data,const int& additionalData)
    : A{ data },m_additinalData{ additionalData }
{
}

void B::setAdditionalData(int additionalData)
{
    m_additinalData = additionalData;
}

std::ostream& B::toStream(std::ostream& s) const
{
    A::toStream(s);
    return s << '\t' << m_additinalData;
}

main.cpp

#pragma once
#include "A.h"

#include <vector>

class ContainerA
{
public:
    void push(A* a);
    size_t getSize() const;
    virtual A* getElement(const int& index);
    virtual A* operator[](const int& index);
protected:
    std::vector<A*> m_As;
};

void ContainerA::push(A* a)
{
    m_As.push_back(a);
}

A* ContainerA::getElement(const int& index)
{
    return m_As[index];
}

A* ContainerA::operator[](const int& index)
{
    return m_As[index];
}

size_t ContainerA::getSize() const
{
    return m_As.size();
}

输出

#pragma once
#include "ContainerA.h"
#include "B.h" // The compiler should be able to tell that B is a subclass of A

class ContainerB : public ContainerA
{
public:
    B* getElement(const int& index) override;
    B* operator[](const int& index) override;
private:
    int additional_data;
};

B* ContainerB::getElement(const int& index)
{
    return static_cast<B*>(m_As[index]);
}

B* ContainerB::operator[](const int& index)
{
    return static_cast<B*>(m_As[index]);
}

现在,您可以将#include "ContainerB.h" int main() { B b1{ 4,-1 }; B b2{ 5,-1 }; B b3{ 6,-1 }; ContainerB contB{}; contB.push(&b1); contB.push(&b2); contB.push(&b3); B* b{ contB.getElement(0) }; b->setAdditionalData(0); size_t size{ contB.getSize() }; for (int i{ 0 }; i < size; ++i) { std::cout << *contB.getElement(i) << std::endl; std::cout << *contB[i] << std::endl; } } 传递给期望4 0 4 0 5 -1 5 -1 6 -1 6 -1 的函数/方法,而不必存储冗余数据。

,

考虑使用静态多态性(模板)。

(代码未经测试)

auto const& inner_vector(ContainerA& ca){return ca.va;}
auto const& inner_vector(ContainerB& cb){return cb.vb;}

template<class TContainer>
double sum(TContainer &c){
   double res = 0;
   for (const auto &a: inner_vector(c)) 
       res += a.x;
   return res;
}