I am doing humble attempts to implement some specific BST with the interface compatible with std::set and the more attempts I do the more questions I have.
For example, one of the questions is how to minimize code duplication while implementing two overloaded versions of insert
method:
std::pair<iterator, bool> insert(const value_type & value)
and
std::pair<iterator,bool> insert(value_type&& value);
Currently I implemented the first overload that internally calls a private method FindNodeByKey:
template<class T, class Compare = std::less<>, class Allocator = std::allocator<T>>
class my_set
{
...
private:
template <class Key>
Node * FindNodeByKey(const Key & key) const
{
Node * x = m_root;
//walk down the tree
while (x != nullptr)
{
if (m_comp(key, x->value))
{
x = x->left;
}
else if (m_comp(x->value, key))
{
x = x->right;
}
else
{
return x;
}
}
return nullptr;
}
Comparer m_comp;
}
where m_comp is a Comparer
instance, Node
is a structure containing BST links and the value of type T, Key
is any type the Comparer
supports and in particular value_type
.
1) What is the optimal way to implement FindNodeByKey
with the parameter of type Key &&
(for using with the second insert
overload)? Is there a way to avoid its code duplication?
2) And should I pass Key &&
to the comparer?
3) Also honestly I do not quite understand why I use std::less<>
, but std::set uses std::less<T>
as the default comparer.
EDIT1:
value_type
is not guaranteed to have copy constructor (be copyable).
You just have both overloads forward to an implementation template:
template<class T, class Compare = std::less<>, class Allocator = std::allocator<T>>
class my_set {
public:
std::pair<iterator, bool> insert(const value_type& value) {
return insert_impl(value);
}
std::pair<iterator, bool> insert(value_type&& value) {
return insert_impl(std::move(value));
}
private:
template <typename V>
std::pair<iterator, bool> insert_impl(V&& value)
{
// all the logic here, just eventually std::forward the value into the right
// spot. We know at this point that V is either const value_type& or value_type
}
};
You definitely shouldn't have a Node * FindNodeByKey(Key&& key) const
overload. It doesn't make sense as you are not using the parameter key
to make a copy of it. You are just using it to pass it (multiple times) to m_comp
(which in turn doesn't make a copy of it).
You generally want const T& x
and T&& x
overloads when your function needs to make one and only one copy of x
. Then you can optimize this in the case where the value passed is a temporary so instead of the copy you do a move.
As for your insert, yes here you can implement both overloads because insert
fits perfectly into the above category. And both overloads should call FindNodeByKey(const Key&)
You can eliminate having to implement one of your overloads by simply calling the other overload from it:
std::pair<iterator, bool> insert(const value_type & value)
{
return insert(value_type(value));
}
As long as your value_type has a copy constructor, this will create a temporary copy of value, and then call the overload of insert that takes an rvalue reference.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.