我正在尝试声明Row
和Column
类,Row
有一个私有std::map
,其中的值指向模板Column
。像这样:
template <typename T>
class DataType {
private:
T type;
};
template <typename T>
class Field {
private:
T value;
DataType<T> value;
};
class Row {
private:
std::map<unsigned long,Field*> column;
};
好吧,我原则上假设Row
类不应该知道我们想要使用哪种Field
(或Column
},即它是否是{第1列中的{1}}或第2列中的Field<int>
但我不确定Field<double>
声明的正确语法是什么,或者Row::column
在这个意义上是否有限我应该使用别的东西。
我向你提出建议,并提前感谢你们。
答案 0 :(得分:24)
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;
};
并考虑在代码中使用智能指针代替原始指针。无论如何,现在的问题是类型信息丢失了 - 无论你指向Field<double>
还是Field<int>
都不知道,只能通过保留某种类型的标志来检测由模板化派生类设置的基础 - 或者通过使用
dynamic_cast<Field<int>*>(field) != 0
但那很难看。特别是因为你想要的是一个价值语义。即你希望能够复制你的行,它会复制其中的所有字段。并且你想要在存储双精度时得到一个双精度 - 而不是先使用RTTI来破解你的派生类型。
这样做的一种方法是使用有区别的联盟。这基本上是一些任意类型的联合,另外还有一个type-flag,它存储当前存储在该字段中的值(例如,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
答案 1 :(得分:1)
现在,要回答你的问题:),字段&lt;&gt;类可以从所有数据类型共享的公共基类继承。这样,诸如列映射之类的容器可以将指针(使共享指针)保持为实例化模板类的派生对象。
答案 2 :(得分:0)
Row< int, float, int>
与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开创了很多智慧(见Modern C++ Design, by Andrei Alexandrescu),但Boost更现代化,更受支持。
通常,您不会遍历字段 - 每个字段都有自己的类型。但是,你确实需要一个FieldBase
。如果您需要这样的界面,那么在内部将字段存储为boost::array<FieldBase, N>
(即Row<int,float,int>
具有boost::array<FieldBase, 3>
)可能也是值得的。但是,您永远不需要dynamic_cast
FieldBase*
。这是一个运行时测试,在编译时你总是知道每个T
的确切Field<T>
。