![](/img/trans.png)
[英]Is this a defect in C++ that std::get<T> ( const std::pair<const T, U>& ) fail to compile due to const T?
[英]C++ How can I move or swap pair<const T, U>?
我正在寫一個 B-Tree 類。 我想支持所有四種情況,例如std::set
、 std::multiset
、 std::map
、 std::multimap
。
我驗證了我的代碼在前兩種情況下可以正常工作。 問題在於后兩者。 他們的聲明是這樣的:
template <typename T>
concept Containable = std::is_same_v<std::remove_cvref_t<T>, T>;
using index_t = std::ptrdiff_t;
template <Containable K, index_t t = 2, typename Comp = std::ranges::less,
typename Alloc = std::allocator<K>>
using BTreeSet = detail::BTreeBase<K, K, t, Comp, false, Alloc>;
template <Containable K, index_t t = 2, typename Comp = std::ranges::less,
typename Alloc = std::allocator<K>>
using BTreeMultiSet = detail::BTreeBase<K, K, t, Comp, true, Alloc>;
template <Containable K, Containable V, index_t t = 2,
typename Comp = std::ranges::less,
typename Alloc = std::allocator<std::pair<const K, V>>>
using BTreeMap = detail::BTreeBase<K, std::pair<const K, V>, t, Comp, false, Alloc>;
template <Containable K, Containable V, index_t t = 2,
typename Comp = std::ranges::less,
typename Alloc = std::allocator<std::pair<const K, V>>>
using BTreeMultiMap =
detail::BTreeBase<K, std::pair<const K, V>, t, Comp, true, Alloc>;
BTreeBase
是這樣的
template <Containable K, typename V, index_t t, typename Comp, bool AllowDup,
typename Alloc>
requires(t >= 2) class BTreeBase {
// ... details ...
};
對於BTreeMap
, value_type
是std::pair<const K, V>
。 對於關聯容器,通過取消引用迭代器更改鍵是不可接受的。
這行讓我很頭疼:
x->keys_[i] = std::move(y->keys_[t - 1]);
它不編譯。 std::iter_swap
或std::swap
不起作用
這里x
和y
是BTreeBase::Node
和keys_
是std::vector<value_type, Alloc>
。
標准庫容器std::map
, std::unordered_map
使用相同的方法,但它們沒有這個問題,因為它基於紅黑樹和哈希表,所以一個節點只有一個鍵,所以你可以移動一個節點,而不是一個鍵。
但 B-Tree 是另一種野獸。 一個節點有許多密鑰,並且應該可以在節點之間移動或交換密鑰。 (用戶仍然不應該被允許從外部更改密鑰)
我該如何處理?
我發現的唯一方法是不使用std::pair<const K, V>
。
這是我更改的設計:
template <typename> struct TreePairRef {};
template <typename T, typename U> struct TreePairRef<std::pair<T, U>> {
using type = std::pair<const T &, U &>;
};
template <typename TreePair>
using PairRefType = TreePairRef<TreePair>::type;
template <typename> struct TreeRefBase {};
template <typename T, typename U> struct TreeRefBase<std::pair<const T&, U&>> {
using type = std::pair<T, U>;
};
template <typename TreeRef>
using RefBaseType = typename TreeRefBase<TreeRef>::type;
更改迭代器:
template <typename T, bool Const, bool isRef> struct BTreeIterator {
using difference_type = std::ptrdiff_t;
using value_type = std::conditional_t<isRef, RefBaseType<T>, T>;
using pointer = std::conditional_t<Const, const value_type*, value_type*>;
using reference = std::conditional_t<Const, const value_type&, std::conditional_t<isRef, T, value_type&>>;
using node_type = Node *;
using iterator_category = std::bidirectional_iterator_tag;
using iterator_concept = std::iterator_category;
樹型設計:
// invariant: V is either K or std::pair<const K, Value> for some Value type.
static constexpr bool is_set_ = std::is_same_v<K, V>;
using value_type = V;
using reference_type = std::conditional_t<is_set_, const V &, PairRefType<V>>;
using iterator_type = BTreeIterator<std::conditional_t<is_set_, value_type, reference_type>, is_set_, !is_set_>;
using const_iterator_type = BTreeIterator<std::conditional_t<is_set_, value_type, reference_type>, true, !is_set_>;
using reverse_iterator_type = std::reverse_iterator<std::iterator_type>;
using const_reverse_iterator_type = std::reverse_iterator<std::const_iterator_type>;
此設計無需更多技巧即可工作
namespace fc = frozenca;
fc::BTreeMap<std::string, int> tree;
tree["a"] = 3;
tree["aaaa"] = 4;
tree["bas"] = 6;
tree["asdf"] = 6;
for (const auto& [k, v] : tree) {
std::cout << k << ' ' << v << '\n';
}
tree["asdf"] = 3333;
std::cout << tree["asdf"] << '\n';
輸出
a 3
aaaa 4
asdf 6
bas 6
3333
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.