18

我正在尝试声明一个Row和一个Column类,其中Row有一个私有std::map值指向一个模板Column。像这样的东西:

template <typename T> class DataType {
  private:
    T type;
};
template <typename T> class Field {
  private:
    T value;
    DataType<T> type;
};
class Row {
  private:
    std::map<unsigned long,Field*> column;
}; 

好吧,我想原则上这个类不应该知道我们想使用Row哪种Field(或),即它是第1 列中的 a 还是第2 列中的 a 。但我不确定什么是正确的语法对于声明,或者如果在这个意义上是有限的,我应该使用别的东西。ColumnField<int>Field<double>Row::columnstd::map

我感谢您的建议,并提前感谢您的建议。

4

3 回答 3

29

Field单独不是一个类型,而是一个模板,它可以生成一系列类型,例如Field<int>Field<double>。所有这些领域都不相关,以至于一个领域以某种方式从另一个领域派生而来。所以你必须在所有这些生成的类型之间建立某种关系。一种方法是使用通用的非模板基类:

class FieldBase { };

template <typename T>
class Field : public FieldBase {
  private:
    T value;
    DataType<T> type;
};
class Row {
  private:
    std::map<unsigned long,FieldBase*> column;
}; 

并考虑在代码中使用智能指针而不是原始指针。无论如何,现在的问题是类型信息丢失了 - 无论您指向 aField<double>还是指向 aField<int>都不再知道,只能通过在模板派生类设置的基础中保留某种类型标志来检测- 或通过询问 RTTI 使用

dynamic_cast<Field<int>*>(field) != 0

但这很丑陋。特别是因为你想要的是一个值语义。即,您希望能够复制您的行,并且它会复制其中的所有字段。并且您希望在存储双精度时获得双精度 - 无需先使用 RTTI 破解您的派生类型。

一种方法是使用有区别的联合。这基本上是一些任意类型的联合,此外还有一个类型标志,它存储当前存储在该字段中的值(例如,是否为双精度、整数、...)。例如:

template <typename T>
class Field {
  private:
    T value;
    DataType<T> type;
};
class Row {
  private:
    std::map<unsigned long, 
             boost::variant< Field<int>, Field<double> > > 
      column;
};

boost::variant 为您完成所有工作。您可以使用访问使其使用正确的重载调用函子。看看它的说明书

于 2009-02-20T10:58:24.780 回答
2
  1. 你有一个错误:你必须在字段中“值”成员(一个可能应该是“类型”)。
  2. 请不要在地图的值中保留原始指针。使用boost::shared_ptr
  3. 此外,您应该有充分的理由编写这样的类,其中已经有大量您可以使用的 DB/表处理代码。因此,如果适用,请考虑使用现有的东西,而不是编写自己的表格处理代码。

现在,回答您的问题 :),Field<> 类可以继承自所有数据类型共享的公共基类。这样,诸如列映射之类的容器可以保留指向模板类实例化的派生对象的指针(使共享指针成为共享指针)。

于 2009-02-20T10:49:09.340 回答
0

ARow< int, float, int>与 a 确实不同Row<int, std::string>。显然,Row<int,float,int>.field<0>应该是Field<int> 一阵子Row<int,float,int>.field<1>应该是一个Field<float>。并且 Row<int,float,int>.field<3>是编译器错误。

最简单的方法是使用 Boost。Loki 开创了很多智能(请参阅Andrei Alexandrescu 的Modern C++ Design),但Boost更现代且得到更好的支持。

通常,您不会遍历字段 - 每个字段都有自己的类型。但是,您确实需要一个FieldBase. boost::array<FieldBase, N>如果您需要这样的接口,那么在内部将字段存储为 a (即Row<int,float,int>有 a )可能也是值得的boost::array<FieldBase, 3>。不过,您永远不需dynamic_cast要这样做FieldBase*。那是一个运行时测试,您总是在编译时知道T每个测试的确切内容。Field<T>

于 2009-02-20T13:24:30.497 回答