简单的问题:我如何让它工作?
struct A {
double whatever;
std::unordered_map<std::string, A> mapToMoreA;
}
g++ 错误:std::pair<_T1, _T2>::second 类型不完整
据我了解,在实例化映射时,编译器需要知道 A 的大小,但它不知道这一点,因为映射是在 A 的声明中声明的,所以解决这个问题的唯一方法是使用指向A(不想那样做)?
简单的问题:我如何让它工作?
struct A {
double whatever;
std::unordered_map<std::string, A> mapToMoreA;
}
g++ 错误:std::pair<_T1, _T2>::second 类型不完整
据我了解,在实例化映射时,编译器需要知道 A 的大小,但它不知道这一点,因为映射是在 A 的声明中声明的,所以解决这个问题的唯一方法是使用指向A(不想那样做)?
大多数情况下,它将取决于容器实现细节(更准确地说,取决于在容器声明时实例化的内容和不实例化的内容)。显然,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
在这方面设计得更好。
除了适用于不完整类型的智能指针之外,我不知道任何 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 有任何性能问题。
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++ 中,您通常使用具有预定义常量大小的指针来表示不完整的类型:
这当然会改变您使用地图的方式:您必须取消引用*
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();
}
如果让地图保持指针是不可接受的,也许这对你有用:
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 更多的限制?)否则,我不确定我在这里遗漏了什么。那些对此表示反对的人,请在评论中启发我。谢谢!