C++将多种类型推送到Vector上

Oli*_*ryn 15 c++ types memory-management vector

注意:我之前已经问过类似的问题,但我没有发现它们有用或非常清楚.

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

我试图看看是否有一种方法可以让每个索引中的单个向量保持多种类型.例如,假设我有以下代码示例:

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);
Run Code Online (Sandbox Code Playgroud)

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

在我的实际应用中,我知道什么是可能的类型可以被插入载体,但这些类型并不都来自同一个超类派生,并没有保证所有这些类型的将被推到载体或以何种顺序.

有没有办法可以安全地完成我在代码示例中演示的目标?

感谢您的时间.

Die*_*ühl 20

由于std::vector<T>需要是同质类型,所以对象保持不变.如果您需要将不同类型的对象放入一个向量中,您需要以某种方式擦除它们的类型并使它们看起来相似.你可以使用道德等同于boost::anyboost::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";
}
Run Code Online (Sandbox Code Playgroud)


Cha*_*had 9

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

这是一个例子,如果我们要做的就是将对象打印到控制台:

#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;
}
Run Code Online (Sandbox Code Playgroud)


Jim*_* Lu 8

为了做到这一点,你肯定需要一个包装类来以某种方式隐藏对象的类型信息.

当你以前在将Type-B存入其中时尝试获得Type-A时,让这个类抛出异常可能也是一件好事.

这是我的一个项目的Holder课程的一部分.你可以从这里开始.

注意:由于使用了不受限制的联合,这仅适用于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.
};
Run Code Online (Sandbox Code Playgroud)

  • 你现在可以使用c ++ 11,它具有所有的优点:p你可以在[维基百科](http://en.wikipedia.org/wiki/C%2B%2B11#Unrestricted_unions)中参考它. (2认同)
  • 你可能应该注意到,这是你的答案中只有C++ 11的解决方案,同时注意到你必须使用placement new和explicit destroy来安全地使用`string`成员. (2认同)