問題描述
基本上我想要 MyClass 持有一個(gè) Hashmap 將字段名稱(字符串)映射到任何類型的值.. 為此,我編寫了一個(gè)單獨(dú)的 MyField 類來保存類型 &價(jià)值信息..
這是我目前所擁有的:
template 類 MyField {T m_Value;int m_Size;}結(jié)構(gòu)我的類{std::map領(lǐng)域;//錯(cuò)誤!!!}
但是正如您所看到的,地圖聲明失敗了,因?yàn)槲覜]有為 MyField 提供類型參數(shù)...
所以我想它必須是這樣的
std::map<字符串,MyField>領(lǐng)域;
或
std::map<字符串,MyField>領(lǐng)域;
但顯然這破壞了我的整個(gè)目的,因?yàn)槁暶鞯牡貓D只能保存特定類型的 MyField ..我想要一個(gè)可以保存任何類型的 MyField 類的地圖..
有什么辦法可以實(shí)現(xiàn)這個(gè)目標(biāo)嗎?
Blindy 的回答很好(+1),但只是為了完成答案:還有另一種方法可以在沒有庫的情況下使用動態(tài)繼承:
class MyFieldInterface{int m_Size;//當(dāng)然在實(shí)際代碼中使用適當(dāng)?shù)脑L問級別...~MyFieldInterface() = 默認(rèn)值;}模板 類 MyField :公共 MyFieldInterface {T m_Value;}結(jié)構(gòu)我的類{std::map領(lǐng)域;}
優(yōu)點(diǎn):
- 任何 C++ 編碼人員都熟悉它
- 它不會強(qiáng)迫您使用 Boost(在某些情況下您不允許使用);
缺點(diǎn):
- 你必須在堆/空閑存儲上分配對象并使用引用語義而不是值語義來操作它們;
- 以這種方式公開的公共繼承可能會導(dǎo)致過度使用動態(tài)繼承以及許多與類型相關(guān)的長期問題,因?yàn)槟念愋痛_實(shí)過于相互依賴;
- 如果指針向量必須擁有對象,則它是有問題的,因?yàn)槟仨毠芾礓N毀;
因此,如果可以,請使用 boost::any 或 boost::variant 作為默認(rèn)值,否則僅考慮使用此選項(xiàng).
要解決最后一個(gè)缺點(diǎn),您可以使用智能指針:
struct MyClass {std::map>領(lǐng)域;//或 shared_ptr<>如果您共享所有權(quán)}
但是還有一個(gè)潛在的問題:
它強(qiáng)制您使用 new/delete(或 make_unique/shared)創(chuàng)建對象.這意味著實(shí)際對象是在分配器提供的任何位置(主要是默認(rèn)位置)的空閑存儲(堆)中創(chuàng)建的.因此,由于
然而,如果您需要保持對象插入的順序,這種技術(shù)就會失去興趣.
無論如何,有幾種可能的解決方案,這在很大程度上取決于您的需求.如果您對自己的案例沒有足夠的經(jīng)驗(yàn),我建議您使用我在示例中首先解釋的簡單解決方案或 boost::any/variant.
<小時(shí)>作為對這個(gè)答案的補(bǔ)充,我想指出非常好的博客文章,這些文章總結(jié)了您可以使用的所有 C++ 類型擦除技術(shù),并附有評論和優(yōu)缺點(diǎn):
- http://talesofcpp.fusionfenix.com/post-16/episode-9-erasing-the-concrete
- http://akrzemi1.wordpress.com/2013/11/18/type-erasure-part-i/
- http://akrzemi1.wordpress.com/2013/12/06/type-erasure-part-ii/
- http://akrzemi1.wordpress.com/2013/12/11/type-erasure-part-iii/
- http://akrzemi1.wordpress.com/2014/01/13/type-erasure-part-iv/
Basically I want MyClass that holds a Hashmap that maps Field name(string) to ANY type of Value.. For this purpose I wrote a separate MyField class that holds the type & value information..
This is what I have so far:
template <typename T>
class MyField {
T m_Value;
int m_Size;
}
struct MyClass {
std::map<string, MyField> fields; //ERROR!!!
}
But as you can see, the map declaration fails because I didn't provide the type parameter for MyField...
So I guess It has to be something like
std::map< string, MyField<int> > fields;
or
std::map< string, MyField<double> > fields;
But obviously this undermines my whole purpose, because the declared map can only hold MyField of a specific type.. I want a map that can hold ANY type of MyField clas..
Is there any way I can achieve this..?
Blindy's answer is very good (+1), but just to complete the answer: there is another way to do it with no library, by using dynamic inheritance:
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;
}
Pros:
- it's familiar to any C++ coder
- it don't force you to use Boost (in some contexts you are not allowed to);
Cons:
- you have to allocate the objects on the heap/free store and use reference semantic instead of value semantic to manipulate them;
- public inheritance exposed that way might lead to over-use of dynamic inheritance and a lot of long-term issues related to your types really being too inter-dependent;
- a vector of pointers is problematic if it have to own the objects, as you have to manage destruction;
So use boost::any or boost::variant as default if you can, and consider this option only otherwise.
To fix that last cons point you could use smart pointers:
struct MyClass {
std::map<string, std::unique_ptr<MyFieldInterface> > fields; // or shared_ptr<> if you are sharing ownership
}
However there is still a potentially more problematic point:
It forces you to create the objects using new/delete (or make_unique/shared). This mean that the actual objects are created in the free store (the heap) at any location provided by the allocator (mostly the default one). Therefore, going though the list of objects very often is not as fast as it could be because of cache misses.
If you are concerned with performance of looping through this list very often as fast as possible (ignore the following if not), then you'd better use either boost::variant (if you already know all the concrete types you will use) OR use some kind of type-erased polymorphic container.
The idea is that the container would manage arrays of objects of the same type, but that still expose the same interface. That interface can be either a concept (using duck-typing techniques) or a dynamic interface (a base class like in my first example). The advantage is that the container will keep same-type objects in separate vectors, so going through them is fast. Only going from one type to another is not.
Here is an example (the images are from there): http://bannalia.blogspot.fr/2014/05/fast-polymorphic-collections.html
However, this technique loose it's interest if you need to keep the order in which the objects are inserted.
In any way, there are several solutions possible, which depends a lot on your needs. If you have not enough experience with your case, I suggest using either the simple solution I first explained in my example or boost::any/variant.
As a complement to this answer, I want to point very good blog articles which summarize all C++ type-erasure techniques you could use, with comments and pros/cons:
- http://talesofcpp.fusionfenix.com/post-16/episode-nine-erasing-the-concrete
- http://akrzemi1.wordpress.com/2013/11/18/type-erasure-part-i/
- http://akrzemi1.wordpress.com/2013/12/06/type-erasure-part-ii/
- http://akrzemi1.wordpress.com/2013/12/11/type-erasure-part-iii/
- http://akrzemi1.wordpress.com/2014/01/13/type-erasure-part-iv/
這篇關(guān)于C++ std::map 持有任何類型的值的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!