简体   繁体   English

构造不同类型的C ++向量数组

[英]Constructing array of C++ vectors of different types

Is there a nice way to construct an array of std::vectors of different types? 有没有一种很好的方法来构造不同类型的std :: vector数组? Also Is there a good way of storing those vectors? 还有存储这些向量的好方法吗?

For example, let us have some structs Foo , Bar , and Baz . 例如,让我们有一些结构FooBarBaz I want to make a container class Cont that hold some combination of Foo , Bar , Baz vectors. 我想制作一个容器类Cont ,其中包含FooBarBaz向量的某种组合。 The following code will achieve this, but I have some problems with it. 以下代码将实现此目的,但是我有一些问题。

#include <vector>

// arbitrary structs
struct Foo{ int var1; };
struct Bar{ double var1; double var2; };
struct Baz{ char var1; float var2; };

enum Mask{
    fooMask = (1<<0),
    barMask = (1<<1),
    bazMask = (1<<2)
};

class Cont{
    void** containers;

public:
    Cont(int mask){
        // count number of 1s in mask
        int count = 0;
        int countMask = mask;
        while(countMask){
            countMask &= countMask-1; // unset rightmost 1
            count++;
        }

        containers = new void*[count];

        int index = 0;
        if((mask & fooMask) == fooMask)
            containers[index++] = new std::vector<Foo>;
        if((mask & barMask) == barMask)
            containers[index++] = new std::vector<Bar>;
        if((mask & bazMask) == bazMask)
            containers[index++] = new std::vector<Baz>;
    }
};

int main(){
    // example construction
    Cont c1(fooMask);
    Cont c2(barMask|bazMask);

    return 0;
}

First, I don't like that I have to store the array of vectors in a void** but I could not figure out a better way. 首先,我不喜欢将向量数组存储在void **中,但是我找不到更好的方法。

Second, if I add a new struct called Qux , I would have to modify the Cont constructor. 其次,如果我添加一个名为Qux的新结构,则必须修改Cont构造函数。 Preferably for maintainability, I would want to construct the array without having to hardcode the struct types into the Cont class. 最好是出于可维护性,我希望构造数组而不必将结构类型硬编码到Cont类中。

I've tried to use templates to solve this problem, but couldn't find a solution I was happy with. 我试图使用模板来解决此问题,但是找不到我满意的解决方案。 I'm concerned about making Cont a template as I think it will lead to template bloat for each combination of structs. 我担心让Cont成为模板,因为我认为这会导致每个结构组合的模板膨胀。 Also I will have multiple Cont objects but only one of each combination that I need. 另外,我将有多个Cont对象,但仅需要一种组合。

You can use type erasure. 您可以使用类型擦除。

struct ContainerBase
{
  virtual ~ContainerBase() = 0;
  // This is where you can add an interface for common functionality.
  // Write the pure virtual functions here and implement/override them in ContainerTyped.
};

inline ContainerBase::~ContainerBase() = default;

template<class T>
struct ContainerTyped : public ContainerBase
{
  std::vector<T> values;
};

class Cont
{
  std::vector<std::unique_ptr<ContainerBase>> containers;

public:
  Cont(int mask) {
    // ...
    if ((mask & fooMask) > 0)
      containers.push_back(std::make_unique<ContainerTyped<Foo>>());
    if ((mask & barMask) > 0)
      containers.push_back(std::make_unique<ContainerTyped<Bar>>());
  }
};

Demo 演示版

This is probably more suitable than using eg std::any or other preexisting type erasure because 1) you specify that only specific things (your vector containers) can be stored, and 2) you can add a common interface as indicated and even specialize the interface functions in the differently ContainerTyped . 这可能比使用例如std::any或其他先前存在的类型擦除更合适,因为1)您指定只能存储特定的东西(您的矢量容器),并且2)您可以按照指示添加通用接口,甚至专门接口函数在不同的ContainerTyped But we would need to know more about your use case to detail this benefit. 但是我们需要更多地了解您的用例,以详细说明这种好处。

The problem with void* is always that you need to somehow retain information about what you actually stored because you are circumventing the strong type system. void*的问题始终是,由于要绕过强类型系统,因此您始终需要以某种方式保留有关实际存储内容的信息。 In other words, how would you get the stored thing back into the strong type system? 换句话说,如何将存储的东西返回到强类型系统中? This is exactly the part where the above approach can shine because you could add a virtual print() = 0 in ContainerBase and then create specialized versions for each kind of struct, eg 这正是上述方法可以发挥作用的部分,因为您可以在ContainerBase添加一个virtual print() = 0 ,然后为每种结构创建专用版本,例如

template<>
void ContainerTyped<Foo>::print()
{
    for (Foo& foo : values) {
        // Print Foo objects as you wish!
    }
}

In terms of not having to touch the Cont constructor when adding a Qux struct, you obviously still need to encode the information of "which mask bit belongs to which struct" somehow, but you can extract it from the Cont constructor (and even hide it in a different translation unit): 就添加Qux结构时不必触摸Cont构造函数而言,您显然仍然需要以某种方式编码“哪个掩码位属于哪个结构”的信息,但是您可以从Cont构造函数中提取它(甚至将其隐藏)在不同的翻译单元中):

// Put this implementation wherever, Cont only has to know the signature.
std::unique_ptr<ContainerBase> makeContainer(int mask, unsigned indexBit)
{
  if ((mask & fooMask) > 0)
    return std::make_unique<ContainerTyped<Foo>>();
  // etc.
  if ((mask & quxMask) > 0)
    return std::make_unique<ContainerTyped<Qux>>();
  return nullptr;
}

// ...

Cont::Cont(int mask)
{
  for (unsigned indexBit = 0; indexBit < 8; ++indexBit) {
    auto container = makeContainer(mask, indexBit);
    if (container)
      containers.emplace_back(std::move(container));
  }
}

You can go about other ways of encoding that enum -> type information, but that's beyond the scope of this question. 您可以采用其他方式来枚举枚举->类型信息,但这超出了此问题的范围。 The key is that you can hide your concrete type behind ContainerBase and use that everywhere you want to refer to "any one of those Containers". 关键是您可以将具体类型隐藏在ContainerBase后面,并在要引用“这些容器中的任何一个”的地方使用该类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM