Я пытаюсь немного лучше понять категории значений в 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
не копируется?
@Someprogrammerdude Спасибо! Теперь это имеет смысл.
Почему значение 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? Я спрашиваю, поскольку мне было непонятно, предлагаете ли вы что-то другое.
@FranciscoCastanheira нет, t
создается с помощью конструктора по умолчанию, std::thread()
- это прямая инициализация . Между ними нет ничего
Из второй отправленной вами ссылки: «При вызове функции каждый параметр ([dcl.fct]) должен быть инициализирован ([dcl.init], [class.copy], [class.ctor]) соответствующим аргументом. " Я написал небольшой пример, чтобы проверить это, и в нем действительно вызывался конструктор перемещения. Напишу об этом в другом комментарии ниже.
@FranciscoCastanheira Я говорю о построении t
(или foo
в вашем другом примере), а не о построении mT
(mFoo
). Это по умолчанию, созданное из инициализатора. Элемент данных создается с помощью перемещения из параметра в конструкторе, да
Да, я это пропустил, спасибо за разъяснения!
Для справки, это другой код, который я написал: Pastebin.com/mG3Ye7hi Обновлено: и который я в конечном итоге удалил, потому что написал его в комментарии, который испортил форматирование.
std::thread
не имеет конструктора копирования, но имеет конструктор rvalue . Именно он будет использоваться для создания передаваемого объекта.