C++ std::map 的模板类值

2021-12-23 00:00:00 methods templates c++ stdmap

我试图声明一个 Row 和一个 Column 类,其中 Row 有一个私有的 std::map 的值指向模板化的 Column.像这样:

I'm attempting to declare a Row and a Column class, with the Row having a private std::map with values pointing to a templated Column. Something like this:

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(或 Column)喜欢使用,即它是第 1 列中的 Field 还是第 2 列中的 Field.但我不确定什么是正确的语法Row::column 声明,或者如果 std::map 在这个意义上是有限的,我应该使用别的东西.

Well, I suppose in principle the Row class shouldn't have to know which kind of Field (or Column) we'd like to use, i.e. whether it's a Field<int> in column 1 or a Field<double> in column 2. But I'm not sure what's the correct syntax for the Row::column declaration, or if the std::map is limited in this sense and I should be using something else.

非常感谢您的建议,并提前感谢您.

I appretiate you suggestions and thank you for them in advance.

推荐答案

Field 本身并不是一个类型,而是一个可以生成一系列类型的模板,例如 Field;Field.所有这些领域都不相关,以至于一个领域以某种方式从另一个领域派生出来.所以你必须在所有这些生成的类型之间建立某种关系.一种方法是使用通用的非模板基类:

Field alone is not a type, but a template which can generate a family of types, such as Field<int> and Field<double>. All these fields are not related such that the one is somehow derived from the other or such. So you have to establish some relation between all these generated types. One way is to use a common non-template base class:

class FieldBase { };

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

并考虑在代码中使用智能指针而不是原始指针.无论如何,现在的问题是类型信息丢失了 - 无论您指向 Field 还是指向 Field 都不再为人所知,并且可以只能通过在模板派生类设置的基类中保留某种类型标志来检测 - 或者通过使用

And consider using smart pointer instead of that raw pointer in the code. Anyway, now the problem is that the type-information is lost - whether you point to a Field<double> or to a Field<int> is not known anymore and can only be detected by keeping some sort of type-flag in the base which is set by the templated derived class - or by asking RTTI using

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

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

But that's ugly. Especially because what you want there is a value semantic. I.e you would want to be able to copy your row, and it would copy all the fields in it. And you would want to get a double when a double is stored - without first using RTTI to hack your way to the derived type.

一种方法是使用歧视联合.这基本上是一些任意类型的联合,此外还有一个类型标志,用于存储当前存储在该字段中的值(例如,是否为 double、int、...).例如:

One way of doing it is to use a discriminated union. That is basically an union for some arbitrary types and in addition a type-flag, which stores what value is currently stored in that field (e.g whether a double, int, ...). For example:

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 为您完成所有工作.您可以使用访问使其使用正确的重载调用函子.看看它的手册

boost::variant does all the work for you. You can use visitation to make it call a functor using the right overload. Have a look at its manual

相关文章