簡體   English   中英

包含自身映射的C ++ Struct

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM