Что происходит в C++, когда rvalue типа, доступного только для перемещения, передается функции по значению?

Я пытаюсь немного лучше понять категории значений в C++ и столкнулся с кодом, который меня немного запутал.

Я рассмотрел некоторые вопросы, такие как Принимать параметр только для перемещения по значению или ссылку на rvalue или Как можно передавать типы, доступные только для перемещения (например, std::unique_ptr) по значению?, но я не нашел ни одного комментария, объясняющего, что происходит под капотом или почему можно передавать в качестве аргументов rvalue типов, доступных только для перемещения, по значению. Возьмите пример ниже:

class Foo
{
  Foo(std::thread t) : mT(std::move(t))
private:
  std::thread mT;
};

int main()
{
  std::thread t;
  Foo f(t); // doesn't compile as std::thread is not copyable
  Foo f{std::thread()}; // compiles just fine using an rvalue
}

Вызов Foo f(t); вызовет несуществующий конструктор-копию std::thread. Однако что происходит внутри, чтобы то же самое не произошло при создании объекта Foo со значением rvalue std::thread()? Почему rvalue не копируется?

std::thread не имеет конструктора копирования, но имеет конструктор rvalue . Именно он будет использоваться для создания передаваемого объекта.
Some programmer dude 20.08.2024 11:31

@Someprogrammerdude Спасибо! Теперь это имеет смысл.

Francisco Castanheira 20.08.2024 11:37
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Почему значение rvalue не копируется?

Выражение std::thread() — это инициализация параметра t. Это то же самое, почему нет копии, когда вы пишете std::thread t;.

В реализации Foo::Foo объект t передается std::move, который приводит его к ссылке rvalue, так что он соответствует std::thread::thread(std::thread&&), то есть конструктору перемещения потока.

«Выражение std::thread() является инициализацией параметра t. Это то же самое, почему нет копии, когда вы пишете std::thread t;». Согласны ли вы с @Someprogrammerdude относительно того, что t создается с помощью конструктора rvalue? Я спрашиваю, поскольку мне было непонятно, предлагаете ли вы что-то другое.

Francisco Castanheira 20.08.2024 13:55

@FranciscoCastanheira нет, t создается с помощью конструктора по умолчанию, std::thread() - это прямая инициализация . Между ними нет ничего

Caleth 20.08.2024 14:20

Из второй отправленной вами ссылки: «При вызове функции каждый параметр ([dcl.fct]) должен быть инициализирован ([dcl.init], [class.copy], [class.ctor]) соответствующим аргументом. " Я написал небольшой пример, чтобы проверить это, и в нем действительно вызывался конструктор перемещения. Напишу об этом в другом комментарии ниже.

Francisco Castanheira 20.08.2024 14:40

@FranciscoCastanheira Я говорю о построении t (или foo в вашем другом примере), а не о построении mT (mFoo). Это по умолчанию, созданное из инициализатора. Элемент данных создается с помощью перемещения из параметра в конструкторе, да

Caleth 20.08.2024 14:44

Да, я это пропустил, спасибо за разъяснения!

Francisco Castanheira 20.08.2024 14:49

Для справки, это другой код, который я написал: Pastebin.com/mG3Ye7hi Обновлено: и который я в конечном итоге удалил, потому что написал его в комментарии, который испортил форматирование.

Francisco Castanheira 20.08.2024 14:51

Другие вопросы по теме