C++17 - 使用自定义分配器的节点提取/重新插入 - 适用于 clang++/libc++,但不适用于 libstd

C++17 - node extraction/reinsertion with custom allocator - works with clang++/libc++ but not libstdc++

本文关键字:适用于 插入 clang++ libstd 不适用 libc++ 新插入 分配器 自定义 节点 C++17      更新时间:2023-10-16

我当前的项目,我正在使用Boost Interprocess库将一些数据存储在共享内存中的unordered_map中。下面是一个最小示例:

#include <unordered_map>
#include <atomic>
#include <iostream>
#include <iterator>
#include <utility>
#include <string>
#include <scoped_allocator>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
namespace ipc = boost::interprocess;
struct Foo {
Foo(size_t _a){
a = _a;
}
size_t a;
std::atomic<bool> deleted {false};
};
/**
* allocator type needed to construct maps in shared memory
*/
template <typename OBJ_T>
using OBJ_ALLOC = ipc::allocator<std::pair<const size_t, OBJ_T>,
ipc::managed_shared_memory::segment_manager>;
/**
* map type to construct maps in shared memory. ideally, only construct the content in-place
* to avoid excessive copying ... 
*/
template <typename OBJ_T>
using OBJ_MAP = std::unordered_map<size_t,
OBJ_T,
std::hash<size_t>,
std::equal_to<size_t>,
std::scoped_allocator_adaptor<OBJ_ALLOC<OBJ_T>>>;
int main(){
OBJ_MAP<Foo> *map;
ipc::managed_shared_memory data_segment(ipc::open_or_create,
std::string("data_segment").c_str(),
4096);
OBJ_ALLOC<Foo> alloc(data_segment.get_segment_manager());
map = data_segment.find_or_construct<OBJ_MAP<Foo>>
(ipc::unique_instance) 
(alloc);
map->emplace(std::piecewise_construct,
std::forward_as_tuple(1),
std::forward_as_tuple(2321));
// before  
for(auto it = map->begin(); it != map->end(); it++) {
std::cout << "Key: " << it->first << " Value: " << it->second.a << std::endl;
}
auto i = map->extract(1);
i.key() = 2;
map->insert(std::move(i));
// after
for(auto it = map->begin(); it != map->end(); it++) {
std::cout << "Key: " << it->first << " Value: " << it->second.a << std::endl;
}
data_segment.destroy<OBJ_MAP<Foo>>(ipc::unique_instance);
ipc::shared_memory_object::remove(std::string("data_segment").c_str());
return 0;  
}

我最初是在macOS上开发这个项目的,所以我针对clang++/libc++ 7.0.0编译,它工作得很好。

clang++ test_map.cpp -std=c++17 -stdlib=libc++ -lpthread -lrt

现在我正在将整个东西移植到 Linux,并在 g++ 和 libstdc++ (g++ 8.2.1( 中出现错误。

g++ test_map.cpp -std=c++17 -lpthread -lrt

节点提取由于隐式删除的复制构造函数而无法编译,并且由于某些不可行的转换而导致重新插入失败,但错误消息非常神秘。

它在 Linux 上使用 clang++/libc++ 运行良好,但这意味着所有依赖库(如 Boost(都需要使用相同的工具链进行编译,这很麻烦。

如何解释这些差异,有没有办法解决它们?

编辑:这是代码:https://gcc.godbolt.org/z/L40Jsl

编辑:和实际的错误消息...

user@computer ~/repos/playground % g++ -std=c++17 test_map.cpp
In file included from /usr/include/c++/8.2.1/unordered_map:46,
from test_map.cpp:1:
/usr/include/c++/8.2.1/bits/hashtable.h: In instantiation of ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::insert_return_type 
std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_M_reinsert_node(std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, 
_H2, _Hash, _RehashPolicy, _Traits>::node_type&&) [with _Key = long unsigned int; _Value = std::pair<const long unsigned int, Foo>; _Alloc = std::scoped_allocator_adaptor<boost::inte
rprocess::allocator<std::pair<const long unsigned int, Foo>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost
::interprocess::iset_index> > >; _ExtractKey = std::__detail::_Select1st; _Equal = std::equal_to<long unsigned int>; _H1 = std::hash<long unsigned int>; _H2 = std::__detail::_Mod_ran
ge_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<false, false, true>; std::_Ha
shtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::insert_return_type = std::_Node_insert_return<std::__detail::_Node_iterator<std::pair<con
st long unsigned int, Foo>, false, false>, std::_Node_handle<long unsigned int, std::pair<const long unsigned int, Foo>, std::scoped_allocator_adaptor<boost::interprocess::allocator<
std::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_
family>, boost::interprocess::iset_index> > > > >; typename std::__allocator_traits_base::__rebind<_Alloc, std::__detail::_Hash_node<_Value, typename _Traits::__hash_cached::value>, 
void>::type = std::scoped_allocator_adaptor<boost::interprocess::allocator<std::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>, boost::interprocess::segment_man
ager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > >; typename std::__detail::_Hashtable_base<_Key, _Value, _Extra
ctKey, _Equal, _H1, _H2, _Hash, _Traits>::iterator = std::__detail::_Node_iterator<std::pair<const long unsigned int, Foo>, false, false>; std::_Hashtable<_Key, _Value, _Alloc, _Extr
actKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::node_type = std::_Node_handle<long unsigned int, std::pair<const long unsigned int, Foo>, std::scoped_allocator_adaptor<boos
t::interprocess::allocator<std::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<
boost::interprocess::mutex_family>, boost::interprocess::iset_index> > > >]’:
/usr/include/c++/8.2.1/bits/unordered_map.h:438:53:   required from ‘std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::insert_return_type std::unordered_map<_Key, _Tp, _Hash, _Pre
d, _Alloc>::insert(std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::node_type&&) [with _Key = long unsigned int; _Tp = Foo; _Hash = std::hash<long unsigned int>; _Pred = std::equ
al_to<long unsigned int>; _Alloc = std::scoped_allocator_adaptor<boost::interprocess::allocator<std::pair<const long unsigned int, Foo>, boost::interprocess::segment_manager<char, bo
ost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::insert_return_type =
std::_Node_insert_return<std::__detail::_Node_iterator<std::pair<const long unsigned int, Foo>, false, false>, std::_Node_handle<long unsigned int, std::pair<const long unsigned int
, Foo>, std::scoped_allocator_adaptor<boost::interprocess::allocator<std::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>, boost::interprocess::segment_manager<c
har, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > > > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::node_type
= std::_Node_handle<long unsigned int, std::pair<const long unsigned int, Foo>, std::scoped_allocator_adaptor<boost::interprocess::allocator<std::__detail::_Hash_node<std::pair<cons
t long unsigned int, Foo>, false>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index
> > > >]’
test_map.cpp:63:27:   required from here
/usr/include/c++/8.2.1/bits/hashtable.h:809:5: error: no matching function for call to ‘std::_Hashtable<long unsigned int, std::pair<const long unsigned int, Foo>, std::scoped_alloca
tor_adaptor<boost::interprocess::allocator<std::pair<const long unsigned int, Foo>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interproces
s::mutex_family>, boost::interprocess::iset_index> > >, std::__detail::_Select1st, std::equal_to<long unsigned int>, std::hash<long unsigned int>, std::__detail::_Mod_range_hashing, 
std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_insert_unique_node(std::_Hashtable<long unsigned 
int, std::pair<const long unsigned int, Foo>, std::scoped_allocator_adaptor<boost::interprocess::allocator<std::pair<const long unsigned int, Foo>, boost::interprocess::segment_manag
er<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > >, std::__detail::_Select1st, std::equal_to<long unsigned int>, s
td::hash<long unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, fals
e, true> >::size_type&, std::_Hashtable<long unsigned int, std::pair<const long unsigned int, Foo>, std::scoped_allocator_adaptor<boost::interprocess::allocator<std::pair<const long 
unsigned int, Foo>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > >, std::__d
etail::_Select1st, std::equal_to<long unsigned int>, std::hash<long unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehas
h_policy, std::__detail::_Hashtable_traits<false, false, true> >::__hash_code&, std::allocator_traits<std::scoped_allocator_adaptor<boost::interprocess::allocator<std::__detail::_Has
h_node<std::pair<const long unsigned int, Foo>, false>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::int
erprocess::iset_index> > > >::pointer&)’
= _M_insert_unique_node(__bkt, __code, __nh._M_ptr);
/usr/include/c++/8.2.1/bits/hashtable.h:1717:5: note: candidate: ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::iterator std::_
Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_M_insert_unique_node(std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _
H2, _Hash, _RehashPolicy, _Traits>::size_type, std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__hash_code, std::_Hashtable<_Key,
_Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__node_type*, std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolic
y, _Traits>::size_type) [with _Key = long unsigned int; _Value = std::pair<const long unsigned int, Foo>; _Alloc = std::scoped_allocator_adaptor<boost::interprocess::allocator<std::p
air<const long unsigned int, Foo>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index
> > >; _ExtractKey = std::__detail::_Select1st; _Equal = std::equal_to<long unsigned int>; _H1 = std::hash<long unsigned int>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::_
_detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<false, false, true>; std::_Hashtable<_Key, _Value, _All
oc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::iterator = std::__detail::_Node_iterator<std::pair<const long unsigned int, Foo>, false, false>; std::_Hashtable<_K
ey, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::size_type = long unsigned int; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2,
_Hash, _RehashPolicy, _Traits>::__hash_code = long unsigned int; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__node_type = s
td::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>; typename _Traits::__hash_cached = std::integral_constant<bool, false>]’
_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
_H1, _H2, _Hash, _RehashPolicy, _Traits>::
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/8.2.1/bits/hashtable.h:1717:5: note:   no known conversion for argument 3 from ‘std::allocator_traits<std::scoped_allocator_adaptor<boost::interprocess::allocator<st
d::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_fa
mily>, boost::interprocess::iset_index> > > >::pointer’ {aka ‘boost::interprocess::offset_ptr<std::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>, long int, lon
g unsigned int, 0>’} to ‘std::_Hashtable<long unsigned int, std::pair<const long unsigned int, Foo>, std::scoped_allocator_adaptor<boost::interprocess::allocator<std::pair<const long
unsigned int, Foo>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > >, std::__
detail::_Select1st, std::equal_to<long unsigned int>, std::hash<long unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_reha
sh_policy, std::__detail::_Hashtable_traits<false, false, true> >::__node_type*’ {aka ‘std::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>*’}

它抱怨对_M_insert_unique_node的调用传递了一个花哨的指针,但_M_insert_unique_node只接受原始指针。看起来像一个库错误。

与其说是修复,不如说是一种解决方法,但用boost::unordered_map替换std::unordered_map可以编译并使用两个工具链。

唯一需要进行的更改:

// use boost instead ... 
#include <boost/unordered_map.hpp>
template <typename OBJ_T>
using OBJ_MAP = boost::unordered_map<size_t,
OBJ_T,
std::hash<size_t>,
std::equal_to<size_t>,
std::scoped_allocator_adaptor<OBJ_ALLOC<OBJ_T>>>;