[英]C++ Struct that contains a map of itself
簡單的問題:我如何讓它工作?
struct A {
double whatever;
std::unordered_map<std::string, A> mapToMoreA;
}
g ++錯誤:std :: pair <_T1,_T2> :: second的類型不完整
據我所知,在實例化地圖時,編譯器需要知道A的大小,但它不知道這個,因為地圖是在A的聲明中聲明的,所以這是解決這個問題的唯一方法來使用指針A(不喜歡這樣做)?
除了使用不完整類型的智能指針之外,我不知道任何STL容器。 如果您不想使用指針,則可以使用包裝器結構:
struct A {
struct B { double whatever; };
std::unordered_map<std::string, B> mapToB;
};
編輯:如果以上不符合您的用例,這是一個指針替代。
struct A {
double whatever;
std::unordered_map<std::string, std::unique_ptr<A>> mapToMoreA;
};
您也可以使用boost::unordered_map
,它不僅支持不完整的類型,而且在Visual Studio中具有更高的調試性能,因為由於過多的迭代器調試檢查,Microsoft的std::unordered_map
實現效率極低。 我不知道任何容器對gcc的任何性能問題。
大多數情況下,它將取決於容器實現細節(更確切地說,取決於在容器聲明點處實例化的內容以及什么不實例化)。 顯然, std::unordered_map
實現需要完成類型。 同時GCC的std::map
實現與不完整類型編譯完美。
為了說明這種差異的來源,請考慮以下示例。 假設我們決定自己實現std::vector
-like功能,並按如下方式聲明我們的vector類
template <typename T> class my_vector {
T *begin;
T *end;
...
};
只要我們的類定義只包含指向T
指針,類型T
就不需要為類定義本身完成。 我們可以將my_vector
本身實例化為一個不完整的T
而沒有任何問題
class X;
my_vector<X> v; // OK
當我們開始使用(並因此實例化) my_vector
的各個方法時, 稍后將需要該類型的“完整性”。
但是,如果由於某種原因我們決定在我們的向量類中包含一個直接的T
實例,那么事情就會變得嚴峻
template <typename T>
class my_vector {
T *begin;
T *end;
T dummy_element;
...
};
現在,在my_vector
本身的實例化時,很早就需要T
的完整性
class X;
my_vector<X> v; // ERROR, incomplete type
在你的情況下必須發生類似的事情。 你正在處理的unordered_map
的定義包含A
的直接實例。 這就是為什么不可能實例化的原因(顯然,在這種情況下你最終會得到無限遞歸類型)。
通過實現unordered_map
更好地考慮將確保不將A
本身包含為直接成員。 這樣的實施不需要A
完成。 正如您所指出的那樣,Boost的unordered_map
實現在這方面設計得更好。
Boost.Variant為此目的明確地提供了一個方便的實用程序 - boost::recusive_wrapper<>
。 以下應該有效:
struct A {
double whatever;
std::unordered_map<std::string, boost::recursive_wrapper<A>> mapToMoreA;
};
唯一值得注意的缺點是Boost.Variant尚未更新以支持C ++ 11移動語義。
更新:在Boost 1.56中添加。
或者使用指向地圖的指針。 在這種情況下,指針必須是void *類型(可以隱藏在一組函數后面)。 也許std :: unordered_map可以替代不完整的值類型。
在C ++中,對於不完整的類型,通常使用具有預定義常量大小的指針:
這當然會改變您使用地圖的方式:您必須使用*
或->
運算符取消引用才能訪問成員,並且必須在某些時候delete
指針。
struct A
{
double bla;
std::map<std::string, A*> mapToMoreA;
};
A的成員函數應該在struct
塊中拆分成原型並稍后實現,否則A及其成員尚未完全定義:
struct A
{
double bla;
std::map<std::string, A*> mapToMoreA;
void doStuff(const std::string& str);
};
void A::doStuff(const std::string& str)
{
mapToMoreA[str] = new A();
}
如果不接受地圖保持指針,也許這對你有用:
struct A {
struct hidden;
std::unique_ptr<hidden> pimpl;
};
struct A::hidden {
double whatever;
std::unordered_map<std::string, A> mapToMoreA;
};
我想你可以直接聲明struct A;
在定義之前,編譯器應該很開心。
編輯:所以在被多次投票后,我寫了下面的內容,看看我錯過了什么:
#include <boost/unordered_map.hpp>
#include <string>
#include <iostream>
struct A;
struct A {
double whatever;
boost::unordered_map<std::string, A> mapToMoreA;
};
int main(void)
{
A b;
b.whatever = 2.5;
b.mapToMoreA["abc"] = b;
std::cerr << b.mapToMoreA["abc"].whatever << std::endl;
return 0;
}
這可以在我的mac上使用g ++ 4.2.1進行編譯,並在運行時打印出“2.5”(如預期的那樣)。
對不起,我沒有unordered_map
沒有提升。 這是問題嗎? (也就是說, std::unordered_map
以某種方式對編譯器施加了比boost更多的限制嗎?)否則,我不確定我在這里缺少關於這個問題的內容。 那些貶低此事,請饒恕我的評論。 謝謝!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.