基本上我想要的是认为,字段名(字符串)映射到任何类型值的一个HashMap为此我写了持有型和价值信息的单独MyField的MyClass类..
这是我到目前为止:
template <typename T>
class MyField {
T m_Value;
int m_Size;
}
struct MyClass {
std::map<string, MyField> fields; //ERROR!!!
}
Run Code Online (Sandbox Code Playgroud)
但正如您所看到的,地图声明失败,因为我没有为MyField提供类型参数...
所以我想它必须是这样的
std::map< string, MyField<int> > fields;
Run Code Online (Sandbox Code Playgroud)
要么
std::map< string, MyField<double> > fields;
Run Code Online (Sandbox Code Playgroud)
但显然这会破坏我的整个目的,因为声明的地图只能保存特定类型的MyField.我想要一个可以容纳任何类型的MyField clas的地图.
有什么办法可以实现这个目的吗?
Kla*_*aim 27
Blindy的答案非常好(+1),但只是为了完成答案:通过使用动态继承,还有另一种方法可以在没有库的情况下完成:
class MyFieldInterface
{
int m_Size; // of course use appropriate access level in the real code...
~MyFieldInterface() = default;
}
template <typename T>
class MyField : public MyFieldInterface {
T m_Value;
}
struct MyClass {
std::map<string, MyFieldInterface* > fields;
}
Run Code Online (Sandbox Code Playgroud)
优点:
缺点:
因此,如果可以,请使用boost :: any或boost :: variant作为默认值,否则仅考虑此选项.
要修复最后一个缺点,你可以使用智能指针:
struct MyClass {
std::map<string, std::unique_ptr<MyFieldInterface> > fields; // or shared_ptr<> if you are sharing ownership
}
Run Code Online (Sandbox Code Playgroud)
但是,仍然存在一个可能更有问题的问题:
它强制您使用new/delete(或make_unique/shared)创建对象.这意味着实际对象是在分配器提供的任何位置(主要是默认值)的空闲存储(堆)中创建的.因此,由于缓存未命中,经常访问对象列表的速度并不快.

如果你经常关注循环遍历此列表的性能(如果没有,则忽略以下内容),那么你最好使用boost :: variant(如果你已经知道将要使用的所有具体类型)或者使用某种类型擦除的多态容器.

这个想法是容器将管理相同类型的对象数组,但仍然暴露相同的接口.该接口可以是一个概念(使用鸭子类型技术)或动态接口(像我的第一个例子中的基类).优点是容器将相同类型的对象保存在单独的向量中,因此通过它们很快.只从一种类型到另一种类型不是.
这是一个例子(图像来自那里):http://bannalia.blogspot.fr/2014/05/fast-polymorphic-collections.html
但是,如果您需要保持插入对象的顺序,这种技术就会失去兴趣.
无论如何,有几种可能的解决方案,这在很大程度上取决于您的需求.如果你对你的案例没有足够的经验,我建议使用我在我的例子中首先解释的简单解决方案或者boost :: any/variant.
作为这个答案的补充,我想指出非常好的博客文章,总结了你可以使用的所有C++类型擦除技术,评论和优点/缺点:
Bli*_*ndy 16
使用boost::variant(如果您知道可以存储的类型,它提供编译时支持)或boost::any(对于任何类型 - 但实际情况不太可能).
http://www.boost.org/doc/libs/1_55_0/doc/html/variant/misc.html#variant.versus-any
编辑:我不能强调,虽然滚动你自己的解决方案似乎很酷,但从长远来看,使用完整,正确的实现将为你节省很多麻烦.boost::any实现RHS拷贝构造函数(C++ 11),包括safectness,RHS操作数以及指针和值类型的safe(typeid())和不安全(dumb casts)值检索const.
这通常是正确的,但对于低级别的基本类型来说更是如此,您可以构建整个应用程序.
Ami*_* G. 13
这在C++ 17中很简单.使用std :: map + std :: any + std :: any_cast:
#include <map>
#include <string>
#include <any>
main()
{
std::map<std::string, std::any> Notebook;
std::string name{ "Pluto" };
int year = 2015;
Notebook["PetName"] = name;
Notebook["Born"] = year;
std::string strS = std::any_cast<std::string>(Notebook["PetName"]); // = "Pluto"
int intI = std::any_cast<int>(Notebook["Born"]); // = 2015
}
Run Code Online (Sandbox Code Playgroud)
class AnyBase
{
public:
virtual ~AnyBase() = 0;
};
inline AnyBase::~AnyBase() {}
template<class T>
class Any : public AnyBase
{
public:
typedef T Type;
explicit Any(const Type& data) : data(data) {}
Any() {}
Type data;
};
std::map<std::string, std::unique_ptr<AnyBase>> anymap;
anymap["number"].reset(new Any<int>(5));
anymap["text"].reset(new Any<std::string>("5"));
// throws std::bad_cast if not really Any<int>
int value = dynamic_cast<Any<int>&>(*anymap["number"]).data;
Run Code Online (Sandbox Code Playgroud)
C++17 有一种std::variant类型,它可以比联合更好地保存不同类型。
对于那些不使用 C++17 的人,boost::variant实现相同的机制。
对于那些不使用 boost 的人,https://github.com/mapbox/variantvariant为 C++11 和 C++14实现了一个更轻量级的版本,它看起来非常有前途,文档齐全,轻量级,并且有大量的使用示例。