unordered_set<Foo> 作为 Foo 的数据成员?
unordered_set<Foo> as data member of Foo?
正如标题所说,我正在尝试使用包含 class Foo 的 objects 的 unordered_set 作为 class Foo 的数据成员。这在 C++ 中可能吗?
我有这个代码:
#include <unordered_set>
using namespace std;
struct FooHash;
class Foo {
public:
int id;
unordered_set<Foo, FooHash> foos; // error here
bool operator==(const Foo& foo) {
return id == foo.id;
}
};
struct FooHash {
size_t operator()(const Foo& foo) const {
return foo.id;
}
};
int main() {
Foo f;
unordered_set<Foo, FooHash> foos;
return 0;
}
但它抛出以下错误:
In file included from /usr/include/c++/6.3.1/bits/hashtable.h:35:0,
from /usr/include/c++/6.3.1/unordered_set:47,
from main.cpp:1:
/usr/include/c++/6.3.1/bits/hashtable_policy.h: In instantiation of ‘struct std::__detail::__is_noexcept_hash<Foo, FooHash>’:
/usr/include/c++/6.3.1/type_traits:143:12: required from ‘struct std::__and_<std::__is_fast_hash<FooHash>, std::__detail::__is_noexcept_hash<Foo, FooHash> >’
/usr/include/c++/6.3.1/type_traits:154:38: required from ‘struct std::__not_<std::__and_<std::__is_fast_hash<FooHash>, std::__detail::__is_noexcept_hash<Foo, FooHash> > >’
/usr/include/c++/6.3.1/bits/unordered_set.h:95:63: required from ‘class std::unordered_set<Foo, FooHash>’
main.cpp:9:33: required from here
/usr/include/c++/6.3.1/bits/hashtable_policy.h:85:34: error: no match for call to ‘(const FooHash) (const Foo&)’
noexcept(declval<const _Hash&>()(declval<const _Key&>()))>
~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/6.3.1/bits/move.h:57:0,
from /usr/include/c++/6.3.1/bits/stl_pair.h:59,
from /usr/include/c++/6.3.1/utility:70,
from /usr/include/c++/6.3.1/unordered_set:38,
from main.cpp:1:
/usr/include/c++/6.3.1/type_traits: In instantiation of ‘struct std::__not_<std::__and_<std::__is_fast_hash<FooHash>, std::__detail::__is_noexcept_hash<Foo, FooHash> > >’:
/usr/include/c++/6.3.1/bits/unordered_set.h:95:63: required from ‘class std::unordered_set<Foo, FooHash>’
main.cpp:9:33: required from here
/usr/include/c++/6.3.1/type_traits:154:38: error: ‘value’ is not a member of ‘std::__and_<std::__is_fast_hash<FooHash>, std::__detail::__is_noexcept_hash<Foo, FooHash> >’
: public integral_constant<bool, !_Pp::value>
前向声明两个 classes 并在之后声明方法给出了这两个错误:
In file included from /usr/include/c++/6.3.1/unordered_set:44:0,
from main.cpp:1:
/usr/include/c++/6.3.1/ext/aligned_buffer.h: In instantiation of ‘struct __gnu_cxx::__aligned_buffer<Foo>’:
/usr/include/c++/6.3.1/bits/hashtable_policy.h:246:43: required from ‘struct std::__detail::_Hash_node_value_base<Foo>’
/usr/include/c++/6.3.1/bits/hashtable_policy.h:277:12: required from ‘struct std::__detail::_Hash_node<Foo, true>’
/usr/include/c++/6.3.1/bits/hashtable_policy.h:1894:60: required from ‘struct std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<Foo, true> > >’
/usr/include/c++/6.3.1/bits/hashtable.h:170:11: required from ‘class std::_Hashtable<Foo, Foo, std::allocator<Foo>, std::__detail::_Identity, std::equal_to<Foo>, FooHash, 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++/6.3.1/bits/unordered_set.h:96:18: required from ‘class std::unordered_set<Foo, FooHash>’
main.cpp:12:37: required from here
/usr/include/c++/6.3.1/ext/aligned_buffer.h:85:34: error: invalid application of ‘sizeof’ to incomplete type ‘Foo’
: std::aligned_storage<sizeof(_Tp), std::alignment_of<_Tp>::value>
^
/usr/include/c++/6.3.1/ext/aligned_buffer.h:85:34: error: invalid application of ‘sizeof’ to incomplete type ‘Foo’
/usr/include/c++/6.3.1/ext/aligned_buffer.h: In instantiation of ‘void* __gnu_cxx::__aligned_buffer<_Tp>::_M_addr() [with _Tp = Foo]’:
/usr/include/c++/6.3.1/ext/aligned_buffer.h:110:41: required from ‘_Tp* __gnu_cxx::__aligned_buffer<_Tp>::_M_ptr() [with _Tp = Foo]’
/usr/include/c++/6.3.1/bits/hashtable_policy.h:250:34: required from ‘_Value* std::__detail::_Hash_node_value_base<_Value>::_M_valptr() [with _Value = Foo]’
/usr/include/c++/6.3.1/bits/hashtable_policy.h:1971:36: required from ‘void std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_deallocate_node(std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type*) [with _NodeAlloc = std::allocator<std::__detail::_Hash_node<Foo, true> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<Foo, true>]’
/usr/include/c++/6.3.1/bits/hashtable_policy.h:1984:22: required from ‘void std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_deallocate_nodes(std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type*) [with _NodeAlloc = std::allocator<std::__detail::_Hash_node<Foo, true> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<Foo, true>]’
/usr/include/c++/6.3.1/bits/hashtable.h:1901:7: required from ‘void std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::clear() [with _Key = Foo; _Value = Foo; _Alloc = std::allocator<Foo>; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<Foo>; _H1 = FooHash; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, true, true>]’
/usr/include/c++/6.3.1/bits/hashtable.h:1227:12: required from ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::~_Hashtable() [with _Key = Foo; _Value = Foo; _Alloc = std::allocator<Foo>; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<Foo>; _H1 = FooHash; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, true, true>]’
/usr/include/c++/6.3.1/bits/unordered_set.h:126:7: required from here
/usr/include/c++/6.3.1/ext/aligned_buffer.h:99:36: error: using invalid field ‘__gnu_cxx::__aligned_buffer<_Tp>::_M_storage’
return static_cast<void*>(&_M_storage);
标准中似乎不允许使用不完整类型的容器。我想尝试使用指针,但是 operator==
不能为指针操作数重载。有什么解决方法吗?
在将它们用作 unordered_set
的模板参数之前,您需要完整定义 类。虽然 Foo
不能完全定义 在 期间它是自己的定义,但它的指针类型是。您可以很容易地将 Foo
替换为 unique_ptr<Foo>
来解决此问题。
#include <memory>
#include <unordered_set>
// Forward declaration
class Foo;
// Declare classes
struct FooHash {
size_t operator()(const std::unique_ptr<Foo>& foo) const;
};
class Foo {
public:
int id;
std::unordered_set<std::unique_ptr<Foo>, FooHash> foos;
};
// Method implementations
size_t FooHash::operator()(const std::unique_ptr<Foo>& foo) const {
return foo->id;
}
正如 François Andrieux 在 中指出的那样,不完整的类型不能与 std::unordered_set 一起使用。他使用 std::unique_ptr
给出了一个可能的解决方案。
使用 std::unique_ptr
的一个缺点可能是您的 class 现在需要 显式动态内存分配 才能插入 Foo::foos
。在我看来,这是一个应该隐藏的实现细节。
另一个(可能更大)问题可能是您 松散的值语义 ,因为 std::unique_ptr 只能移动,不能复制。您必须实现自定义复制构造函数和赋值运算符才能恢复值语义。
使用boost::recursive_wrapper,还有另一种可能的解决方案隐藏指针。虽然 boost::recursive_wrapper
是为 boost::variant
发明的,但它可以用于其他不允许不完整类型的情况。
使用 recursive_wrapper,Foo class 变为:
class Foo {
public:
using Wrapper = boost::recursive_wrapper<Foo>;
Foo() = default;
explicit Foo( int id ) : id( id ) {}
struct Hash {
size_t operator()(const Wrapper& foo) const { return foo.get().id; }
};
struct KeyEqual {
bool operator()(const Wrapper& foo1, const Wrapper& foo2) const {
return foo1.get().id == foo2.get().id;
}
};
using Set = std::unordered_set< Wrapper, Hash, KeyEqual >;
Set foos;
int id = 0;
};
我通过使用 std::unordered_set
的第三个模板参数 KeyEqual 进行了另一个更改,以独立于 Foo::operator==
定义键的相等性。现在 Foo::operator==
可以通过比较 Foo 的 所有 成员,而不仅仅是 id 来实现它应该的样子。这留作 reader.
的练习
要在 Foo::foos
中插入和查找项目,您可以定期创建 Foo 的实例,而不必使用动态内存分配。在幕后,boost::recursive_wrapper
会动态分配内存。
唯一会注意到使用包装器的地方是当您通过 Foo::Set
的迭代器访问 Foo 时,因为那时您必须调用 boost::reference_wrapper::get()
。这并不比使用需要使用双重间接寻址(即 (*it)->id
)的 std::unique_ptr
差。
用法示例:
int main() {
Foo f1;
// No explicit dynamic memory allocation here!
f1.foos.insert( Foo( 1 ) );
f1.foos.insert( Foo( 1 ) );
f1.foos.insert( Foo( 2 ) );
// Value semantics are kept.
Foo f2 = f1;
f2.foos.erase( Foo( 1 ) );
std::cout << "f1.size: " << f1.foos.size() << std::endl;
std::cout << "f2.size: " << f2.foos.size() << std::endl;
// Find a Foo, still clean syntax.
auto it = f1.foos.find( Foo( 2 ) );
if( it != f1.foos.end() )
{
// Only when accessing Foo through the iterator we notice
// the recursive_wrapper because we must call its get() method.
std::cout << "found a Foo with id: " << it->get().id << std::endl;
}
return 0;
}
正如标题所说,我正在尝试使用包含 class Foo 的 objects 的 unordered_set 作为 class Foo 的数据成员。这在 C++ 中可能吗?
我有这个代码:
#include <unordered_set>
using namespace std;
struct FooHash;
class Foo {
public:
int id;
unordered_set<Foo, FooHash> foos; // error here
bool operator==(const Foo& foo) {
return id == foo.id;
}
};
struct FooHash {
size_t operator()(const Foo& foo) const {
return foo.id;
}
};
int main() {
Foo f;
unordered_set<Foo, FooHash> foos;
return 0;
}
但它抛出以下错误:
In file included from /usr/include/c++/6.3.1/bits/hashtable.h:35:0,
from /usr/include/c++/6.3.1/unordered_set:47,
from main.cpp:1:
/usr/include/c++/6.3.1/bits/hashtable_policy.h: In instantiation of ‘struct std::__detail::__is_noexcept_hash<Foo, FooHash>’:
/usr/include/c++/6.3.1/type_traits:143:12: required from ‘struct std::__and_<std::__is_fast_hash<FooHash>, std::__detail::__is_noexcept_hash<Foo, FooHash> >’
/usr/include/c++/6.3.1/type_traits:154:38: required from ‘struct std::__not_<std::__and_<std::__is_fast_hash<FooHash>, std::__detail::__is_noexcept_hash<Foo, FooHash> > >’
/usr/include/c++/6.3.1/bits/unordered_set.h:95:63: required from ‘class std::unordered_set<Foo, FooHash>’
main.cpp:9:33: required from here
/usr/include/c++/6.3.1/bits/hashtable_policy.h:85:34: error: no match for call to ‘(const FooHash) (const Foo&)’
noexcept(declval<const _Hash&>()(declval<const _Key&>()))>
~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/6.3.1/bits/move.h:57:0,
from /usr/include/c++/6.3.1/bits/stl_pair.h:59,
from /usr/include/c++/6.3.1/utility:70,
from /usr/include/c++/6.3.1/unordered_set:38,
from main.cpp:1:
/usr/include/c++/6.3.1/type_traits: In instantiation of ‘struct std::__not_<std::__and_<std::__is_fast_hash<FooHash>, std::__detail::__is_noexcept_hash<Foo, FooHash> > >’:
/usr/include/c++/6.3.1/bits/unordered_set.h:95:63: required from ‘class std::unordered_set<Foo, FooHash>’
main.cpp:9:33: required from here
/usr/include/c++/6.3.1/type_traits:154:38: error: ‘value’ is not a member of ‘std::__and_<std::__is_fast_hash<FooHash>, std::__detail::__is_noexcept_hash<Foo, FooHash> >’
: public integral_constant<bool, !_Pp::value>
前向声明两个 classes 并在之后声明方法给出了这两个错误:
In file included from /usr/include/c++/6.3.1/unordered_set:44:0,
from main.cpp:1:
/usr/include/c++/6.3.1/ext/aligned_buffer.h: In instantiation of ‘struct __gnu_cxx::__aligned_buffer<Foo>’:
/usr/include/c++/6.3.1/bits/hashtable_policy.h:246:43: required from ‘struct std::__detail::_Hash_node_value_base<Foo>’
/usr/include/c++/6.3.1/bits/hashtable_policy.h:277:12: required from ‘struct std::__detail::_Hash_node<Foo, true>’
/usr/include/c++/6.3.1/bits/hashtable_policy.h:1894:60: required from ‘struct std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<Foo, true> > >’
/usr/include/c++/6.3.1/bits/hashtable.h:170:11: required from ‘class std::_Hashtable<Foo, Foo, std::allocator<Foo>, std::__detail::_Identity, std::equal_to<Foo>, FooHash, 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++/6.3.1/bits/unordered_set.h:96:18: required from ‘class std::unordered_set<Foo, FooHash>’
main.cpp:12:37: required from here
/usr/include/c++/6.3.1/ext/aligned_buffer.h:85:34: error: invalid application of ‘sizeof’ to incomplete type ‘Foo’
: std::aligned_storage<sizeof(_Tp), std::alignment_of<_Tp>::value>
^
/usr/include/c++/6.3.1/ext/aligned_buffer.h:85:34: error: invalid application of ‘sizeof’ to incomplete type ‘Foo’
/usr/include/c++/6.3.1/ext/aligned_buffer.h: In instantiation of ‘void* __gnu_cxx::__aligned_buffer<_Tp>::_M_addr() [with _Tp = Foo]’:
/usr/include/c++/6.3.1/ext/aligned_buffer.h:110:41: required from ‘_Tp* __gnu_cxx::__aligned_buffer<_Tp>::_M_ptr() [with _Tp = Foo]’
/usr/include/c++/6.3.1/bits/hashtable_policy.h:250:34: required from ‘_Value* std::__detail::_Hash_node_value_base<_Value>::_M_valptr() [with _Value = Foo]’
/usr/include/c++/6.3.1/bits/hashtable_policy.h:1971:36: required from ‘void std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_deallocate_node(std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type*) [with _NodeAlloc = std::allocator<std::__detail::_Hash_node<Foo, true> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<Foo, true>]’
/usr/include/c++/6.3.1/bits/hashtable_policy.h:1984:22: required from ‘void std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_deallocate_nodes(std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type*) [with _NodeAlloc = std::allocator<std::__detail::_Hash_node<Foo, true> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<Foo, true>]’
/usr/include/c++/6.3.1/bits/hashtable.h:1901:7: required from ‘void std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::clear() [with _Key = Foo; _Value = Foo; _Alloc = std::allocator<Foo>; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<Foo>; _H1 = FooHash; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, true, true>]’
/usr/include/c++/6.3.1/bits/hashtable.h:1227:12: required from ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::~_Hashtable() [with _Key = Foo; _Value = Foo; _Alloc = std::allocator<Foo>; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<Foo>; _H1 = FooHash; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, true, true>]’
/usr/include/c++/6.3.1/bits/unordered_set.h:126:7: required from here
/usr/include/c++/6.3.1/ext/aligned_buffer.h:99:36: error: using invalid field ‘__gnu_cxx::__aligned_buffer<_Tp>::_M_storage’
return static_cast<void*>(&_M_storage);
标准中似乎不允许使用不完整类型的容器。我想尝试使用指针,但是 operator==
不能为指针操作数重载。有什么解决方法吗?
在将它们用作 unordered_set
的模板参数之前,您需要完整定义 类。虽然 Foo
不能完全定义 在 期间它是自己的定义,但它的指针类型是。您可以很容易地将 Foo
替换为 unique_ptr<Foo>
来解决此问题。
#include <memory>
#include <unordered_set>
// Forward declaration
class Foo;
// Declare classes
struct FooHash {
size_t operator()(const std::unique_ptr<Foo>& foo) const;
};
class Foo {
public:
int id;
std::unordered_set<std::unique_ptr<Foo>, FooHash> foos;
};
// Method implementations
size_t FooHash::operator()(const std::unique_ptr<Foo>& foo) const {
return foo->id;
}
正如 François Andrieux 在 std::unique_ptr
给出了一个可能的解决方案。
使用 std::unique_ptr
的一个缺点可能是您的 class 现在需要 显式动态内存分配 才能插入 Foo::foos
。在我看来,这是一个应该隐藏的实现细节。
另一个(可能更大)问题可能是您 松散的值语义 ,因为 std::unique_ptr 只能移动,不能复制。您必须实现自定义复制构造函数和赋值运算符才能恢复值语义。
使用boost::recursive_wrapper,还有另一种可能的解决方案隐藏指针。虽然 boost::recursive_wrapper
是为 boost::variant
发明的,但它可以用于其他不允许不完整类型的情况。
使用 recursive_wrapper,Foo class 变为:
class Foo {
public:
using Wrapper = boost::recursive_wrapper<Foo>;
Foo() = default;
explicit Foo( int id ) : id( id ) {}
struct Hash {
size_t operator()(const Wrapper& foo) const { return foo.get().id; }
};
struct KeyEqual {
bool operator()(const Wrapper& foo1, const Wrapper& foo2) const {
return foo1.get().id == foo2.get().id;
}
};
using Set = std::unordered_set< Wrapper, Hash, KeyEqual >;
Set foos;
int id = 0;
};
我通过使用 std::unordered_set
的第三个模板参数 KeyEqual 进行了另一个更改,以独立于 Foo::operator==
定义键的相等性。现在 Foo::operator==
可以通过比较 Foo 的 所有 成员,而不仅仅是 id 来实现它应该的样子。这留作 reader.
要在 Foo::foos
中插入和查找项目,您可以定期创建 Foo 的实例,而不必使用动态内存分配。在幕后,boost::recursive_wrapper
会动态分配内存。
唯一会注意到使用包装器的地方是当您通过 Foo::Set
的迭代器访问 Foo 时,因为那时您必须调用 boost::reference_wrapper::get()
。这并不比使用需要使用双重间接寻址(即 (*it)->id
)的 std::unique_ptr
差。
用法示例:
int main() {
Foo f1;
// No explicit dynamic memory allocation here!
f1.foos.insert( Foo( 1 ) );
f1.foos.insert( Foo( 1 ) );
f1.foos.insert( Foo( 2 ) );
// Value semantics are kept.
Foo f2 = f1;
f2.foos.erase( Foo( 1 ) );
std::cout << "f1.size: " << f1.foos.size() << std::endl;
std::cout << "f2.size: " << f2.foos.size() << std::endl;
// Find a Foo, still clean syntax.
auto it = f1.foos.find( Foo( 2 ) );
if( it != f1.foos.end() )
{
// Only when accessing Foo through the iterator we notice
// the recursive_wrapper because we must call its get() method.
std::cout << "found a Foo with id: " << it->get().id << std::endl;
}
return 0;
}