Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/29.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;包含任何类型值的映射_C++_Templates_Stl_Stdmap_Stdany - Fatal编程技术网

C++ C++;包含任何类型值的映射

C++ C++;包含任何类型值的映射,c++,templates,stl,stdmap,stdany,C++,Templates,Stl,Stdmap,Stdany,基本上,我希望MyClass持有一个Hashmap,该Hashmap将字段名(字符串)映射到任何类型的 价值为此,我编写了一个单独的MyField类,它保存类型和值信息 这就是我到目前为止所做的: template <typename T> class MyField { T m_Value; int m_Size; } struct MyClass { std::map<string, MyField> fields; //ERROR!!

基本上,我希望MyClass持有一个Hashmap,该Hashmap将字段名(字符串)映射到任何类型的 价值为此,我编写了一个单独的MyField类,它保存类型和值信息

这就是我到目前为止所做的:

template <typename T>
class MyField {
    T m_Value;
    int m_Size;
}


struct MyClass {
    std::map<string, MyField> fields;   //ERROR!!!
}
模板
类MyField{
T m_值;
国际货币单位大小;
}
结构MyClass{
std::映射字段;//错误!!!
}
但正如您所看到的,映射声明失败,因为我没有为MyField提供类型参数

所以我想应该是这样的

std::map< string, MyField<int> > fields;
std::map字段;

std::map字段;

但这显然破坏了我的全部目的,因为声明的映射只能保存特定类型的MyField。。我想要一张能容纳任何类型MyField类别的地图


有什么方法可以实现这一点吗?

使用
boost::variant
(如果您知道可以存储的类型,它提供编译时支持)或
boost::any
(对于真正的任何类型,但这不太可能)

编辑:尽管滚动您自己的解决方案看起来很酷,但从长远来看,使用完整、正确的实现将为您节省大量的麻烦,这一点我再强调也不为过<代码>boost::any实现RHS复制构造函数(C++11),包括安全(
typeid()
)和不安全(哑类型转换)值检索,以及
const
corectness、RHS操作数以及指针和值类型

一般来说,这是正确的,但对于构建整个应用程序所基于的低级基本类型,情况更是如此。

classanybase
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;
{ 公众: virtual~AnyBase()=0; }; 内联AnyBase::~AnyBase(){} 模板 类Any:公共AnyBase { 公众: T型; 显式任意(常量类型和数据):数据(数据){} Any(){} 类型数据; }; std::map anymap; anymap[“number”]。重置(新的任意(5)); anymap[“文本”]。重置(新的任何(“5”)); //抛出std::如果不是真的抛出,则抛出错误 int value=dynamic_cast(*anymap[“number”])。数据;
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;  
}
类MyFieldInterface
{
int m_Size;//当然,在实际代码中使用适当的访问级别。。。
~MyFieldInterface()=默认值;
}
模板
类MyField:公共MyFieldInterface{
T m_值;
}
结构MyClass{
std::映射字段;
}
优点:

    任何C++编码器都是熟悉的
  • 它不会强迫你使用Boost(在某些情况下你是不允许的)
缺点:

  • 您必须在堆/空闲存储上分配对象,并使用引用语义而不是值语义来操作它们
  • 以这种方式暴露的公共继承可能会导致过度使用动态继承,以及与类型相关的许多长期问题实际上过于相互依赖
  • 指针向量如果必须拥有对象,就有问题,因为你必须管理破坏

使用Booo::任何或Booost::如果可以,变量为默认值,仅考虑此选项。

要修复最后一点,可以使用智能指针:

struct MyClass {
    std::map<string, std::unique_ptr<MyFieldInterface> > fields;  // or shared_ptr<> if you are sharing ownership
}
struct MyClass{
std::map fields;//或共享\u ptr(如果共享所有权)
}
然而,还有一个潜在的更大问题:

它强制您使用新建/删除(或使_唯一/共享)创建对象。这意味着实际对象是在分配器提供的任意位置(主要是默认位置)的空闲存储(堆)中创建的。因此,经常浏览对象列表的速度并不如它可能的快,因为

如果您关心尽可能快地循环此列表的性能(如果没有,请忽略以下内容),那么您最好使用boost::variant(如果您已经知道要使用的所有具体类型)或某种类型的擦除多态容器

其思想是容器将管理相同类型的对象数组,但仍然公开相同的接口。该接口可以是一个概念(使用duck类型技术)或一个动态接口(如我的第一个示例中的基类)。 其优点是容器将相同类型的对象保存在单独的向量中,因此快速遍历它们。仅仅从一种类型转换到另一种类型是不可能的

下面是一个示例(图像来自于此):

然而,如果您需要保持对象插入的顺序,那么这种技术就失去了它的兴趣

无论如何,有几种可能的解决方案,这在很大程度上取决于您的需求。如果您对您的案例没有足够的经验,我建议使用我在示例中首先解释的简单解决方案或boost::any/variant


作为这个答案的补充,我想指出非常好的博客文章,它总结了C++中使用的所有类型的删除技术,包括注释和优点:


您还可以使用void*并使用reinterpret\u cast将值转换回正确的类型。这是C语言在回调中经常使用的一种技术

#include <iostream>
#include <unordered_map>
#include <string>
#include <cstdint> // Needed for intptr_t
using namespace std;


enum TypeID {
    TYPE_INT,
    TYPE_CHAR_PTR,
    TYPE_MYFIELD
};    

struct MyField {
    int typeId;
    void * data;
};

int main() {

    std::unordered_map<std::string, MyField> map;

    MyField anInt = {TYPE_INT, reinterpret_cast<void*>(42) };

    char cstr[] = "Jolly good";
    MyField aCString = { TYPE_CHAR_PTR, cstr };

    MyField aStruct  = { TYPE_MYFIELD, &anInt };

    map.emplace( "Int", anInt );
    map.emplace( "C String", aCString );
    map.emplace( "MyField" , aStruct  );  

    int         intval   = static_cast<int>(reinterpret_cast<intptr_t>(map["Int"].data)); 
    const char *cstr2    = reinterpret_cast<const char *>( map["C String"].data );
    MyField*    myStruct = reinterpret_cast<MyField*>( map["MyField"].data );

    cout << intval << '\n'
         << cstr << '\n'
         << myStruct->typeId << ": " << static_cast<int>(reinterpret_cast<intptr_t>(myStruct->data)) << endl;
}
#包括
#包括
#包括
#包括//输入所需
使用名称空间std;
枚举类型ID{
类型_INT,
类型_CHAR _PTR,
类型_MYFIELD
};    
结构MyField{
int-typeId;
作废*数据;
};
int main(){
std::无序地图;
MyField anInt={TYPE_INT,reinterpret_cast(42)};
char cstr[]=“非常好”;
MyField aCString={TYPE_CHAR_PTR,cstr};
MyField aStruct={TYPE_MyField,&anInt};
地图安放位置(“Int”,anInt);
地图放置(“C字符串”,AC字符串);
地图安放位置(“MyField”,aStruct);
int intval=static_cast(重新解释_cast(map[“int”].data));
const char*cstr2=重新解释强制转换(映射[“C Strin
#include <iostream>
#include <unordered_map>
#include <string>
#include <cstdint> // Needed for intptr_t
using namespace std;


enum TypeID {
    TYPE_INT,
    TYPE_CHAR_PTR,
    TYPE_MYFIELD
};    

struct MyField {
    int typeId;
    void * data;
};

int main() {

    std::unordered_map<std::string, MyField> map;

    MyField anInt = {TYPE_INT, reinterpret_cast<void*>(42) };

    char cstr[] = "Jolly good";
    MyField aCString = { TYPE_CHAR_PTR, cstr };

    MyField aStruct  = { TYPE_MYFIELD, &anInt };

    map.emplace( "Int", anInt );
    map.emplace( "C String", aCString );
    map.emplace( "MyField" , aStruct  );  

    int         intval   = static_cast<int>(reinterpret_cast<intptr_t>(map["Int"].data)); 
    const char *cstr2    = reinterpret_cast<const char *>( map["C String"].data );
    MyField*    myStruct = reinterpret_cast<MyField*>( map["MyField"].data );

    cout << intval << '\n'
         << cstr << '\n'
         << myStruct->typeId << ": " << static_cast<int>(reinterpret_cast<intptr_t>(myStruct->data)) << endl;
}
#include <map>
#include <string>
#include <any>
        
int main()
{
    std::map<std::string, std::any> notebook;

    std::string name{ "Pluto" };
    int year = 2015;

    notebook["PetName"] = name;
    notebook["Born"] = year;

    std::string name2 = std::any_cast<std::string>(notebook["PetName"]); // = "Pluto"
    int year2 = std::any_cast<int>(notebook["Born"]); // = 2015
}