【问题标题】:Container of Miscellaneous Types - C++各种类型的容器 - C++
【发布时间】:2015-03-26 14:13:45
【问题描述】:

所以我试图在 C++ 中创建一个“表”类,其结构如下:

表格.h

class Table
{
private:
    class TableImpl;
    TableImpl* impl;
};

表格.cpp

class Table::TableImpl
{
private:
    class Row
    {
    private:
        template <typename T>
        class RowDataCell
        {
            T data;
        }
        std::vector<RowDataCell*> data;
    };
    std::vector<Row*> rows;
};

TableImpl 由 std::vector 和 Row 对象组成,每个 Row 对象由 std::vector 和通用 RowDataCell 对象组成。唯一的问题是,我无法创建std::vector,因为我需要为RowDataCell* 提供一个模板参数,这将阻碍我拥有一个包含杂项对象的容器的目标。

有没有一种方法可以使用标准 C++ 来实现这个目标。

【问题讨论】:

  • 看看 boost::any (并在此处放置模板 - 尽管 - boost::variant 也可能是一个选项)
  • 您永远不需要通用对象的容器。你可以做一个,但它会很难使用。更好的是每种类型的单独容器,或者boost::variant的容器。

标签: c++ templates c++11 generics containers


【解决方案1】:

有两种合理的方法。

第一个是区分联合,另一个是旧 C 风格 void* "anything could be here" 的类型安全变体。

我将首先提到它们的两个boost 实现:

boost::variant&lt;A,B,C&gt;(和传入的std::experimental::variant)是一个有区别的联合。它可以存储A、B 或C 类型的东西。有多种类型安全的方法可以将元素取出,或者通过“访问”对它们执行操作。 Variant 对它可以容纳的类型有一些限制,而更多限制取决于您注入这些类型的方式。

boost::any(和传入的std::experimental::any)是具有值语义的类型安全void*。几乎任何东西都可以存储在其中(any 要求您的对象是 CopyConstructable),但只有在您知道其中存储的确切类型并提出要求时才能访问它。

自己编写任何一个都是可行的,但我建议使用它们,或者至少理解它们并复制它们的界面和模式的大部分。

variant 可以在其内部“内部”存储实例,这通常是一种更好的方法。您可以使用union、类型列表和该列表的索引以及一堆元编程样板来模拟它。顺便说一句,对齐问题很棘手。

any 更容易编写,但仍然有点挑战。它是一个非常基本的类型擦除对象,只有“转换为 X 类型”(通过 typeid 或等效项)并暴露了副本。如果您曾经见过std::function 被实现,那么您已经成功了一半。

【讨论】:

    【解决方案2】:

    是的,有一种标准的 C++ 方法来实现记录表,但它会变得很丑。我试过了。

    让我们假设一个表是一个记录容器。
    记录可以包含字段或记录。
    字段可以是不同的类型,例如整数、字符串和 BLOB。

    至少对我而言,目标是尽可能保持通用性,直到最低级别,即专业领域。这意味着字段值是通过字符串传递的。

    所以,这是一个简化的模型:

    struct Component; // The base of everything.
    struct Record : public Component {
      std::vector<Component *> components;
    };
    struct Field : public Component {
      std::string  name;
      virtual std::string  get_value_as_string(void) = 0;
    };
    struct Field_String : public Field;
    //... And so on.
    struct Table {
      std::vector<Record *> rows;
    };
    

    根本问题是表格的每个单元格都可能属于不同的类型。表格可以有无限的列和行。

    我还建议阅读有关数据库的内容。许多数据库接口可以解决表的问题。

    【讨论】:

      【解决方案3】:

      有几种方法可以做你想做的事。一种方法是为要放入容器的类型定义一个通用接口,然后将指向该接口的指针放入向量中:

      class RowDataCellInterface
      {
      public:
          virtual ~RowDataInterface() {}
          virtual std::string toString() const = 0;
          virtual std::unique_ptr<RowDataInterface> clone() const = 0;
      };
      

      然后您甚至可以以模板化的方式派生该接口的实现:

      template <typename T>
      class RowDataCell final : public RowDataCellInterface
      {
      public:
          virtual std::string toString() const
          { 
              std::stringstream ss; 
              ss << data; 
              return ss.str();
          }
      
          virtual std::unique_ptr<RowDataInterface> clone() const
          {
              std::make_unique<RowDataCell>(*this);
          }
      
      private:
          T data;
      };
      

      现在可以像这样将这些东西放入一个向量中:

      std::vector<std::unique_ptr<RowDataCellInterface>>
      

      一切都应该正常。要复制向量,您应该使用接口的clone() 成员函数。有几种图书馆解决方案可以帮助您完成您想要的。其中之一是我建议的boost::variant。我对一个非常相似的问题here给出了非常详细的答案。另一个库解决方案是boost::any,起初它可能看起来更简单,但因为为了用它做一些有意义的事情,你必须知道包含的东西的类型,它在访问时被删除。大多数时候,我更喜欢boost::variant 而不是boost::any。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-01
        • 1970-01-01
        • 2022-01-13
        • 1970-01-01
        相关资源
        最近更新 更多