能否声明包含自身std::unordered_set成员的C++类?
问题描述
我想声明如下形式的类:
class A { std::unordered_set<A> set; };
我知道需要为A实现哈希函数,但这不是核心问题。真正的问题是std::unordered_set的构造函数因A是不完全类型而报错,前向声明也无法解决这个问题,我不确定还有其他可行方案。
最小复现示例
class A; template <> struct std::hash<A> { // 仅声明,暂不定义,因A尚未定义 size_t operator()( const A& ) const; }; class A { public: bool operator==( const A& ) const { return true; } std::unordered_set<A> set; }; inline size_t std::hash<A>::operator()( const A& ) const { return 0; }
编译错误信息(GCC 8.3.1,C++14)
/usr/include/c++/8/ext/aligned_buffer.h: In instantiation of struct __gnu_cxx::__aligned_buffer<A>: /usr/include/c++/8/bits/hashtable_policy.h:234:43: required from struct std::__detail::_Hash_node_value_base<A> /usr/include/c++/8/bits/hashtable_policy.h:265:12: required from struct std::__detail::_Hash_node<A, true> /usr/include/c++/8/bits/hashtable_policy.h:2038:49: required from struct std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<A, true>> > /usr/include/c++/8/bits/hashtable.h:173:11: required from class std::_Hashtable<A, A, std::allocator<A>, std::__detail::_Identity, std::equal_to<A>, std::hash<A>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, true, true> > /usr/include/c++/8/bits/unordered_set.h:100:18: required from class std::unordered_set<A> /usr/include/c++/8/ext/aligned_buffer.h:91:28: error: invalid application of sizeof to incomplete type : std::aligned_storage<sizeof(_Tp), __alignof__(_Tp)>
除了使用unordered_set<A*>这类方案外,是否还有其他方法可以实现?
解决方案
除了指针方案,还有以下几种可行思路:
1. 用智能指针延迟容器实例化
把成员变量改为std::unique_ptr<std::unordered_set<A>>,这样在类A的定义阶段,std::unordered_set<A>不需要完全实例化——智能指针只依赖前向声明的类型。后续在构造函数中初始化智能指针,此时A已经是完整类型:
#include <memory> #include <unordered_set> class A; template <> struct std::hash<A> { size_t operator()(const A&) const; }; class A { public: bool operator==(const A&) const { return true; } std::unique_ptr<std::unordered_set<A>> set; A() : set(std::make_unique<std::unordered_set<A>>()) {} }; inline size_t std::hash<A>::operator()(const A&) const { return 0; }
2. 封装自定义间接容器
自己实现一个轻量容器类,内部持有unordered_set<A>的指针,对外暴露和std::unordered_set一致的接口,隐藏间接层细节。本质和智能指针方案类似,但能让外部代码更自然地使用。
3. 升级编译器与C++标准(依赖实现)
部分编译器在C17及以后对部分容器放松了不完全类型的限制,比如GCC 10+允许std::vector使用不完全类型,但std::unordered_set的支持仍属于编译器扩展,并非标准强制要求。如果升级到更高版本GCC并启用C17或以上标准,可以尝试直接使用,但这种方式不保证跨编译器可移植。
4. 使用Boost容器库
boost::container::unordered_set明确支持不完全类型作为元素,只要在实际操作容器(如插入元素、调用成员函数)时A已经是完整类型即可。这种方案需要引入Boost依赖,但属于成熟可靠的第三方解决方案。
内容的提问来源于stack exchange,提问作者user1806566
相关产品推荐
相关产品推荐

