8

简单的问题:我如何让它工作?

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

g++ 错误:std::pair<_T1, _T2>::second 类型不完整

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

4

7 回答 7

6

大多数情况下,它将取决于容器实现细节(更准确地说,取决于在容器声明时实例化的内容和不实例化的内容)。显然,std::unordered_map实现需要类型是完整的。同时 GCC 的std::map编译实现与不完整的类型完美结合。

为了说明这种差异的来源,请考虑以下示例。假设我们决定自己实现std::vector-like 功能的幼稚实现,并声明我们的向量类如下

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是完整的。正如您自己指出的那样,Boost 的实现unordered_map在这方面设计得更好。

于 2012-07-11T18:45:21.797 回答
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 中也具有更好的调试性能,std::unordered_map因为由于过多的迭代器调试检查,Microsoft 的实现效率非常低。我不知道任何一个容器的 gcc 有任何性能问题。

于 2012-07-11T18:36:59.757 回答
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 中添加。

于 2012-07-11T19:07:45.877 回答
0

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

于 2012-07-11T18:32:36.873 回答
0

在 C++ 中,您通常使用具有预定义常量大小的指针来表示不完整的类型:

这当然会改变您使用地图的方式:您必须取消引用*or->运算符才能访问成员,并且必须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();
}
于 2012-07-11T18:37:49.553 回答
0

如果让地图保持指针是不可接受的,也许这对你有用:

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

struct A::hidden {
  double whatever;
  std::unordered_map<std::string, A> mapToMoreA;
};
于 2012-07-11T18:42:20.977 回答
-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 更多的限制?)否则,我不确定我在这里遗漏了什么。那些对此表示反对的人,请在评论中启发我。谢谢!

于 2012-07-11T18:29:30.697 回答