Lvalue для ссылки на ссылочную привязку

Компилятор продолжает жаловаться. Я пытаюсь привязать lvalue к ссылке rvalue, но я не вижу, как это сделать. Я новичок в С++ 11, двигаю семантику и т.д., Поэтому, пожалуйста, несите меня.

У меня есть эта функция:

template <typename Key, typename Value, typename HashFunction, typename Equals>
Value& FastHash<Key, Value, HashFunction, Equals>::operator[](Key&& key)
{
    //  Some code here...

    Insert(key, Value()); // Compiler error here

    //   More code here.
}

который вызывает этот метод:

template <typename Key, typename Value, typename HashFunction, typename Equals>
void FastHash<Key, Value, HashFunction, Equals>::Insert(Key&& key, Value&& value)
{
    // ...
}

Я продолжаю получать ошибки, как показано ниже:

cannot convert argument 1 from 'std::string' to 'std::string &&'

в вызове Insert(). Разве не key определяется как rvalue в перегрузке оператора? Почему он переинтерпретируется как lvalue?

Спасибо.

Ответ 1

Insert(key, Value()); // Compiler error here

key здесь Key&& key - это значение l! У него есть имя, и вы можете взять его адрес. Это только тот тип этого lvalue - "rvalue reference to key".

Вам нужно передать значение rvalue, и для этого вам нужно использовать std::move:

Insert(std::move(key), Value()); // No compiler error any more

Я понимаю, почему это противоречит интуиции! Но как только вы различаете и ссылку rvalue (которая является ссылкой, привязанной к rvalue) и фактическим значением r, становится понятнее.

Изменить: настоящая проблема здесь - использовать ссылки rvalue. Имеет смысл использовать их в шаблоне функций, где выводится тип аргумента, поскольку это позволяет аргументу связываться с ссылкой на lvalue или ссылкой на rvalue из-за правил сбрасывания ссылок. См. Эту статью и видео для почему: http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers

Однако в этом случае тип ключа не выводится при вызове функции, поскольку он уже был определен классом при создании экземпляра FastHash<std::string, ... >. Таким образом, вы действительно назначаете использование ссылок rvalue и, таким образом, используя std::move, исправляет код.

Я бы изменил ваш код на то, что параметры берутся по значению:

template <typename Key, typename Value, typename HashFunction, typename Equals>
Value& FastHash<Key, Value, HashFunction, Equals>::operator[](Key key)
{
    //  Some code here...

    Insert(std::move(key), Value());

    //   More code here.
}

template <typename Key, typename Value, typename HashFunction, typename Equals>
void FastHash<Key, Value, HashFunction, Equals>::Insert(Key key, Value value)
{
    // ...
}

Не беспокойтесь о дополнительных копиях из-за использования аргументов значения - они часто оптимизируются компилятором.