Question

In elaboration of my previous question and question, I'd like to understand what goes on in this real scenario. I have the following template function:

template <typename Key, typename Value, typename HashFunction, typename Equals>
void FastHash<Key, Value, HashFunction, Equals>::Insert(const Key& key, const Value& value)
{
    Insert(std::make_pair(key, value));
}

called with, for instance, a mixture of lvalues and rvalues, like in this call:

std::string name = "The Great";
hashTable.Insert(name, "Gatsby");

(for testing purposes). Insert above calls

template <typename Key, typename Value, typename HashFunction, typename Equals>
void FastHash<Key, Value, HashFunction, Equals>::Insert(pair<const Key, Value>&& keyValuePair)
{
    if (buckets.size() == 0)
    {
        buckets.resize(1);
    }

    HashFunction hash;
    unsigned long hashValue = hash(keyValuePair.first) % buckets.size();
    buckets[hashValue].push_back(std::move(keyValuePair));
}

A few questions:

1. I would expect making a pair one of whose elements is a literal string to be undefined behaviour, owing to passing by reference. Is that so?

2. When I step into the make_pair line, the code first calls make_pair(_Ty1&& _Val1, _Ty2&& _Val2), so it seems that the compiler is interpreting key and value as rvalues. Why?

3. The next call before going into the second Insert method is pair(pair<_Other1, _Other2>&& _Right). This happens irrespective of whether the second Insert takes an && or a const &. What's going on here?

4. Tied to this last, should the second Insert take a const pair& or a pair&&, given what it does?

Update: After watching Scott Meyer's excellent video on universal references, reading on template deduction and reference collapsing rules, and with your help I can answer 1, 2, and 4. But I still cannot understand why the pair's move constructor is called just before the Insert call. Any help on that?

Était-ce utile?

La solution

The next call before going into the second Insert method is pair(pair<_Other1, _Other2>&& _Right). This happens irrespective of whether the second Insert takes an && or a const &. What's going on here?

That is std::pair's converting constructor: it's converting the pair that you pass - std::make_pair(key, value) - from std::pair<Key, Value> to the second Insert functions's parameter type std::pair<const Key, Value>. You could avoid the conversion if you specify the pair type yourself instead of having std::make_pair deduce it:

Insert(std::pair<const Key, Value>{key, value});

Of course, this is copying the parameters into a pair, and in C++11 we have the rule of thumb that if you are going to copy something you should accept it by value. So maybe implement this Insert as:

template <typename Key, typename Value, typename HashFunction, typename Equals>
void FastHash<Key, Value, HashFunction, Equals>::Insert(Key key, Value value)
{
    Insert(std::pair<const Key, Value>{std::move(key), std::move(value)});
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top