包含自身映射的C ++ Struct

时间:2012-07-11 18:24:00

标签: c++ syntax compiler-errors

简单的问题:我如何让它发挥作用?

struct A {
    double whatever; 
    std::unordered_map<std::string, A> mapToMoreA; 
}

g ++错误:std :: pair&lt; _T1,_T2&gt; :: second的类型不完整

据我所知,在实例化地图时,编译器需要知道A的大小,但是它不知道这个,因为地图是在A的声明中声明的,所以这是解决这个问题的唯一方法。使用指针A(不想这样做)?

7 个答案:

答案 0 :(得分:2)

除了使用不完整类型的智能指针之外,我不知道任何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的任何性能问题。

答案 1 :(得分:2)

大多数情况下,它将取决于容器实现细节(更确切地说,取决于在容器声明点处实例化的内容以及什么不实例化)。显然,std::unordered_map实现要求类型完整。与此同时,GCC的std::map实现与不完整类型完全匹配。

为了说明这种差异的来源,请考虑以下示例。假设我们决定制作我们自己的std::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; ... }; 的直接实例,那么事情就会变得很糟糕

T

现在my_vector的完整性将在class X; my_vector<X> v; // ERROR, incomplete type 本身的实例化时很早就需要

unordered_map

在你的情况下必须发生类似的事情。您正在处理的A的定义以某种方式包含unordered_map的直接实例。这就是为什么不可能实例化的原因(显然,在这种情况下你最终会得到无限递归类型)。

通过A的实施更好地考虑将确保不将A作为直接成员包括在内。这样的实现不需要unordered_map完成。正如您自己所指出的那样,Boost在{{1}}的实现在这方面设计得更好。

答案 2 :(得分:2)

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中添加。

答案 3 :(得分:0)

或使用指向地图的指针。在这种情况下,指针必须是void *类型(可以隐藏在一组函数后面)。也许std :: unordered_map可以替代不完整的值类型。

答案 4 :(得分:0)

在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();
}

答案 5 :(得分:0)

如果不接受地图保持指针,也许这对您有用:

struct A {
  struct hidden;
  std::unique_ptr<hidden> pimpl;
};

struct A::hidden {
  double whatever;
  std::unordered_map<std::string, A> mapToMoreA;
};

答案 6 :(得分:-2)

我认为您可以在定义之前转发声明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更多的约束?)否则,我不确定我在这里对这个问题缺少什么。那些贬低此事,请饶恕我的评论。谢谢!