Вызов функции, которая перемещает значение параметра в функцию, которая его копирует - PullRequest
0 голосов
/ 04 августа 2020

Я передал следующий код:

void enqueue(T&& value)
{
    move_value_somewhere( T(std::move(value)));
}

void enqueue(const T& value)
{
    enqueue(T(value));
}

Вторая функция, копирующая входной параметр, вызывает первую функцию, которая его перемещает. Предполагая, что код правильный, почему T(std::move(value)) не перемещает значение вместо его копирования, если оно вызывается из второй функции, вероятно, из T(), но я не знаю почему.

Ответы [ 3 ]

3 голосов
/ 04 августа 2020

В enqueue(T(value)); вы сначала создаете временную копию переданного значения (T(value)). Затем эта временная копия перемещается вашим конструктором перемещения enqeue. Таким образом, этот код похож на

T copy(value);
enqueue(std::move(copy));
1 голос
/ 04 августа 2020

В void enqueue(const T& value) временная T создается копией из value в качестве входных данных. Затем этот временный элемент передается в void enqueue(T&& value).

В void enqueue(T&& value) другой временный T создается с перемещением из value в качестве входных данных (при условии, что T имеет конструктор перемещения, иначе он будет вместо этого создаваться копированием). Затем это временное значение передается на move_value_somewhere(). Этот временный T на самом деле не нужен, поскольку value уже является ссылкой rvalue, поэтому его можно напрямую переместить в move_value_somewhere(), например:

void enqueue(T&& value)
{
    move_value_somewhere(std::move(value));
}

Конструктор перемещения вызывается внутри конструктора копирования

На самом деле это не так, потому что показанный код не предназначен для конструкторов вообще, просто для нормального нестатические c методы класса . Тут большая разница. Конструкторы не имеют возвращаемых значений и не могут вызывать друг друга, как этот код. Но они могут делегировать друг другу, но только из списка инициализации членов. См. Конструкторы и списки инициализаторов членов .

1 голос
/ 04 августа 2020

enqueue() принимает T&&, который просто ограничивает его этим типом, вызов конструктора не происходит в результате передачи аргумента, потому что он не является типом значения.

Когда вы вызываете enqueue() однако вы создаете временный T, который будет вызывать его конструктор копирования из const T& value, который вы ему передаете.

Впоследствии временный становится параметром для enqueue(), который, в свою очередь, , затем создает другое prvalue T, на этот раз инициализированное вашим (теперь) параметром lvalue, приведенным к T&&, который, если T имеет конструктор перемещения, вызовет его.

В противном случае, если T не имеет конструктора перемещения (и не имеет явно удаленного конструктора), он «откатится» к вызову конструктора копирования, если он присутствует.

Поскольку вы передаете временное значение move_value_somewhere() ему нужно будет принять либо T&&, const T&, или T - при условии, что тип T имеет жизнеспособный конструктор, чтобы сделать все это возможным.

...