C ++连接多态对象的容器

时间:2017-07-06 20:10:13

标签: c++ c++11 polymorphism containers

假设我有一个虚拟基类和一些派生的具体类:

class Base { ... }
class DerivedA : public Base { ... }
class DerivedB : public Base { ... }
class DerivedC : public Base { ... }

在某些地方,我有每个派生类的对象向量:

std::vector<DerivedA> my_a;
std::vector<DerivedB> my_b;
std::vector<DerivedC> my_c;

现在,我经常需要迭代所有三个向量中的所有元素并运用基类接口。我可以编写三个for循环,每个循环完全相同。但显然这远非最佳解决方案。

有没有一种聪明的方法将向量连接到具有基类指针/引用的公共容器中,这样我只需要迭代一次?或任何其他想法如何优雅地解决这个问题?

5 个答案:

答案 0 :(得分:16)

在目前的情况下,不需要多态性。您只需使用可变参数模板 + 高阶函数来迭代向量。这是使用 fold表达式的C ++ 17解决方案:

template <typename F, typename... Vectors>
void for_all_vectors(F&& f, Vectors&&... vs)
{
    (std::for_each(std::forward<Vectors>(vs).begin(), 
                   std::forward<Vectors>(vs).end(), 
                   f), ...);
}

用法:

int main()
{
    std::vector<A> my_a;
    std::vector<B> my_b;
    std::vector<C> my_c;

    for_all_vectors([](const auto& x){ something(x); }, my_a, my_b, my_c);
}

live example on wandbox

在C ++ 11/14中,您可以使用for_each_argument替换折叠表达式:

template <typename TF, typename... Ts>
void for_each_argument(TF&& f, Ts&&... xs)
{
    return (void)std::initializer_list<int>{
        (f(std::forward<Ts>(xs)), 0)...};
}

template <typename F, typename... Vectors>
void for_all_vectors(F&& f, Vectors&&... vs)
{
    for_each_argument([&f](auto&& v)
    { 
        std::for_each(v.begin(), v.end(), f);
    }, std::forward<Vectors>(vs)...);
}

live example on wandbox

我解释了这个片段背后的想法,并在2015年CppCon演讲中对其进行了扩展:"for_each_argument explained and expanded"

答案 1 :(得分:4)

只需指向基类。你不能有一个类型为base的向量并将派生类放入其中,因为它们可能不是相同的大小,相同的函数等。

所以我要做的是创建一个vector或type base *然后你可以连接派生类的指针。

可能看起来像:

vector<base*> v;
v.push_back(&derivedClassVariableA);
v.push_back(&derivedClassVariableB);

然后,只要您要使用的函数在基础中是虚拟的并且在派生中定义,您应该很高兴

答案 2 :(得分:4)

一个简单的解决方案是使用模板函数迭代向量的成员并调用相应的函数:

class Base {
public:
    virtual int getX() = 0;
};

class Derived1 : public Base {
public:
    int x1=1;
    virtual int getX() { return x1; };
};

class Derived2 : public Base {
public:
    int x2=2;
    virtual int getX() { return x2; };
};

template<typename T>
void callPolymorphic(std::vector<T> &v) {
    for (T& a : v) {
        cout << a.getX() << " ";
    }
}

int main() {

    std::vector<Derived1> my_1(5);
    std::vector<Derived2> my_2(5);

    callPolymorphic(my_1);
    callPolymorphic(my_2);

    return 0;
}

答案 3 :(得分:2)

我只想创建一个函数模板或一个普通的lambda并调用它三次:

auto my_loop = [](auto& vec){
  for (auto& base : vec) {
      // do something with base...
  }
};
my_loop(my_a);
my_loop(my_b);
my_loop(my_c);

答案 4 :(得分:1)

另一方面,您可以制作自己的自制视图适配器:

#include <iostream>
#include <vector>
#include <functional>
#include <iterator>

struct Base {
    virtual int f() const = 0;
    virtual ~Base() {}
};

struct D1: Base {
    int f() const { return 42; }
};

struct D2: Base {
    int f() const { return 314; }
};

template<typename T, typename... Left, typename... Right>
inline std::vector<T, Left...> operator+(std::vector<T, Left...> &&left, std::vector<T, Right...> &&right) {
    std::vector<T, Left...> retVal(std::move(left));
    using mi = std::move_iterator<typename std::vector<T, Right...>::iterator>;
    retVal.insert(retVal.end(), mi(right.begin()), mi(right.end()));
    return retVal;
}

int main() {
    std::vector<D1> v1(3);
    std::vector<D2> v2(4);
    using View = std::vector<std::reference_wrapper<const Base>>;
    View b(View(v1.cbegin(), v1.cend()) + View(v2.cbegin(), v2.cend()));
    for(Base const &item: b) std::cout << item.f() << std::endl;
}

(请注意,基础查看的容器可以是任何,{,1}只是例如,但它们的元素类型应该符合。)