简体   繁体   中英

Implement a function with the argument of both types const T & and T&&

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM