C++ 将多种类型推送到向量上

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13461869/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:21:33  来源:igfitidea点击:

C++ Push Multiple Types onto Vector

c++memory-managementtypesvector

提问by Oliver Spryn

Note:I know similar questions to this have been asked on SO before, but I did not find them helpful or very clear.

注意:我知道之前已经在 SO 上问过类似的问题,但我没有发现它们有帮助或非常清楚。

Second note:For the scope of this project/assignment, I'm trying to avoid third party libraries, such as Boost.

第二个注意事项:对于此项目/作业的范围,我试图避免使用第三方库,例如 Boost。

I am trying to see if there is a way I can have a single vector hold multiple types, in each of its indices. For example, say I have the following code sample:

我想看看是否有一种方法可以让单个向量在其每个索引中包含多种类型。例如,假设我有以下代码示例:

vector<something magical to hold various types> vec;
int x = 3;
string hi = "Hello World";
MyStruct s = {3, "Hi", 4.01};

vec.push_back(x);
vec.push_back(hi);
vec.push_back(s);

I've heard vector<void*>could work, but then it gets tricky with memory allocation and then there is always the possibility that certain portions in nearby memory could be unintentionally overridden if a value inserted into a certain index is larger than expected.

我听说vector<void*>可以工作,但是内存分配会变得棘手,如果插入到某个索引中的值大于预期,那么附近内存中的某些部分总是有可能被无意覆盖。

In my actual application, I know what possibletypes may be inserted into a vector, but these types do not all derive from the same super class, and there is no guarantee that all of these types will be pushed onto the vector or in what order.

在我的实际应用中,我知道有哪些可能的类型可以插入到一个向量中,但是这些类型并不都来自同一个超类,并且不能保证所有这些类型都会被推送到向量中或以什么顺序.

Is there a way that I can safelyaccomplish the objective I demonstrated in my code sample?

有没有一种方法可以安全地实现我在代码示例中演示的目标?

Thank you for your time.

感谢您的时间。

采纳答案by Jimmy Lu

In order to do that, you'll definitely need a wrapper class to somehow conceal the type information of your objects from the vector.

为了做到这一点,您肯定需要一个包装类来以某种方式从向量中隐藏对象的类型信息。

It's probably also good to have this class throw an exception when you try to get Type-A back when you have previously stored a Type-B into it.

当您之前将 Type-B 存储到 Type-A 中时,让此类在尝试取回 Type-A 时抛出异常可能也很好。

Here is part of the Holder class from one of my projects. You can probably start from here.

这是我的一个项目中 Holder 类的一部分。你大概可以从这里开始。

Note: due to the use of unrestricted unions, this only works in C++11.More information about this can be found here: What are Unrestricted Unions proposed in C++11?

注意:由于使用了不受限制的联合,这仅适用于 C++11。可以在此处找到有关此的更多信息:C++11 中提议的无限制联合是什么?

class Holder {
public:
    enum Type {
        BOOL,
        INT,
        STRING,
        // Other types you want to store into vector.
    };

    template<typename T>
    Holder (Type type, T val);

    ~Holder () {
        // You want to properly destroy
        // union members below that have non-trivial constructors
    }

    operator bool () const {
        if (type_ != BOOL) {
           throw SomeException();
        }
        return impl_.bool_;
    }
    // Do the same for other operators
    // Or maybe use templates?

private:
    union Impl {
        bool   bool_;
        int    int_;
        string string_;

        Impl() { new(&string_) string; }
    } impl_;

    Type type_;

    // Other stuff.
};

回答by Dietmar Kühl

The objects hold by the std::vector<T>need to be of a homogenous type. If you need to put objects of different type into one vector you need somehow erase their type and make them all look similar. You could use the moral equivalent of boost::anyor boost::variant<...>. The idea of boost::anyis to encapsulate a type hierarchy, storing a pointer to the base but pointing to a templatized derived. A very rough and incomplete outline looks something like this:

对象持有的std::vector<T>需要是同质类型。如果您需要将不同类型的对象放入一个向量中,您需要以某种方式擦除它们的类型并使它们看起来都相似。您可以使用boost::anyor的道德等价物boost::variant<...>。的想法boost::any是封装一个类型层次结构,存储一个指向基类但指向模板化派生类的指针。一个非常粗略和不完整的轮廓看起来像这样:

#include <algorithm>
#include <iostream>

class any
{
private:
    struct base {
        virtual ~base() {}
        virtual base* clone() const = 0;
    };
    template <typename T>
    struct data: base {
        data(T const& value): value_(value) {}
        base* clone() const { return new data<T>(*this); }
        T value_;
    };
    base* ptr_;
public:
    template <typename T> any(T const& value): ptr_(new data<T>(value)) {}
    any(any const& other): ptr_(other.ptr_->clone()) {}
    any& operator= (any const& other) {
        any(other).swap(*this);
        return *this;
    }
    ~any() { delete this->ptr_; }
    void swap(any& other) { std::swap(this->ptr_, other.ptr_); }

    template <typename T>
    T& get() {
        return dynamic_cast<data<T>&>(*this->ptr_).value_;
    }
};

int main()
{
    any a0(17);
    any a1(3.14);
    try { a0.get<double>(); } catch (...) {}
    a0 = a1;
    std::cout << a0.get<double>() << "\n";
}

回答by Chad

As suggested you can use various forms of unions, variants, etc. Depending on what you want to do with your stored objects, external polymorphism could do exactly what you want, if you can define all necessary operations in a base class interface.

正如所建议的那样,您可以使用各种形式的联合、变体等。根据您想对存储的对象做什么,如果您可以在基类 interface 中定义所有必要的操作,外部多态性可以完全按照您的要求执行

Here's an example if all we want to do is print the objects to the console:

如果我们想要做的只是将对象打印到控制台,那么这是一个示例:

#include <iostream>
#include <string>
#include <vector>
#include <memory>

class any_type
{
public:
   virtual ~any_type() {}
   virtual void print() = 0;
};

template <class T>
class concrete_type : public any_type
{
public:
   concrete_type(const T& value) : value_(value)
   {}

   virtual void print()
   {
      std::cout << value_ << '\n';
   }
private:
   T value_;
};

int main()
{
   std::vector<std::unique_ptr<any_type>> v(2);

   v[0].reset(new concrete_type<int>(99));
   v[1].reset(new concrete_type<std::string>("Bottles of Beer"));

   for(size_t x = 0; x < 2; ++x)
   {
      v[x]->print();
   }

   return 0;
}