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

Posted

技术标签:

【中文标题】模板类值的 C++ std::map【英文标题】:C++ std::map of template-class values 【发布时间】:2010-10-08 19:38:00 【问题描述】:

我正在尝试声明一个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(或Column),即它是第1 列中的Field&lt;int&gt; 还是第 2 列中的 Field&lt;double&gt;。但我不确定 Row::column 声明的正确语法是什么,或者 std::map 在这个意义上是否受到限制,我应该使用其他东西。

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

【问题讨论】:

您不必将代码转换为 html。就这样吧,缩进 4 个字符。 致戴夫: 我的问题是:由于字段是模板化的,我如何“告诉”std::map 这些值是“任何类型的字段”? 致 litb: 感谢您的建议! :-) 【参考方案1】:

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

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&lt;double&gt; 还是 Field&lt;int&gt; 都不再为人所知,只能通过在基础中保留某种类型标志来检测,即由模板化派生类设置 - 或通过使用

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

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

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

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

【讨论】:

值得一提的是,从 c++17 开始,std::variant 已被纳入标准。因此,不再需要 boost 库。 带有基类的解决方案,可以保存事先不知道的类型。【参考方案2】:
    您在此处遇到错误:您必须在字段中“赋值”成员(可能应该是“类型”)。 请不要在映射值中保留原始指针。使用boost::shared_ptr。 此外,您应该有充分的理由编写这样的类,因为那里已经有大量可以使用的 DB/表处理代码。因此,如果适用,请考虑使用现有的东西,而不是编写自己的表格处理代码。

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

【讨论】:

1.对不起,你是对的,我的意思是:T 值; DataType 一个类型; 2. 我不熟悉 Boost 库(我想这就是您所说的),但我会研究一下,谢谢。 3. 能否请您指出我可以研究的一两个建议?【参考方案3】:

Row&lt; int, float, int&gt;Row&lt;int, std::string&gt; 确实不同。 显然,Row&lt;int,float,int&gt;.field&lt;0&gt; 应该是 Field&lt;int&gt;Row&lt;int,float,int&gt;.field&lt;1&gt; 应该是 Field&lt;float&gt;。而Row&lt;int,float,int&gt;.field&lt;3&gt; 是编译器错误。

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

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

【讨论】:

以上是关于模板类值的 C++ std::map的主要内容,如果未能解决你的问题,请参考以下文章

c++模板和迭代器

使用 C++ 函数模板时的转换错误

如何将 std::unordered_map 部分专门化为我的模板类的成员?

C++开发之using定义模板别名

C++开发之using定义模板别名

C++开发之using定义模板别名