Переместить конструктор для std:: mutex

У многих классов стандартной библиотеки С++ теперь есть конструкторы перемещения, например -

thread::thread(thread&& t)

Но похоже, что std:: mutex этого не делает. Я понимаю, что они не могут быть скопированы, но, похоже, имеет смысл возвращать один из функции "make_mutex", например. (Не сказать, что это полезно, просто это имеет смысл)

Есть ли причина, почему std:: mutex не имеет конструктора перемещения?

Ответ 1

Ну... в основном потому, что я не думаю, что они должны двигаться. Буквально.

В некоторых OS-es мьютекс может быть смоделирован как дескриптор (так что вы можете их скопировать), но IIRC mutex pthreads управляется на месте. Если вы собираетесь переместить это, любой поток безопасности будет летать прямо из окна (как другие потоки знают, что мьютекс только что изменил его адрес памяти...)?

Ответ 2

Помните, что С++ воспринимает философию "не платите за то, что вы не используете". Рассмотрим, например, мнимую платформу, которая использует непрозрачный тип для представления мьютекса; позвоните этому типу mutex_t. Если интерфейс для работы с этим мьютеком использует mutex_t* в качестве аргументов, например, как void mutex_init(mutex_t* mutex); для "создания" мьютекса, вполне может случиться так, что адрес мьютекса - это то, что используется для однозначной идентификации мьютекса. Если это так, то это означает, что mutex_t не может быть скопирован:

mutex_t kaboom()
{
    mutex_t mutex;
    mutex_init(&mutex);
    return mutex; // disaster
}

Здесь нет гарантии при выполнении mutex_t mutex = kaboom();, что &mutex - это то же значение, что и &mutex в функциональном блоке.

Когда наступает день, когда разработчик хочет записать std::mutex для этой платформы, если требования этого типа будут подвижными, то это означает, что внутренний mutex_t должен быть помещен в динамически распределенную память, причем все связанные штрафы.

С другой стороны, хотя прямо сейчас std::mutex не движется, очень легко "вернуть" один из функции: вместо этого верните std::unique_ptr<std::mutex>. Это по-прежнему оплачивает затраты на динамическое размещение, но только в одном месте. Весь другой код, который не нужно перемещать std::mutex, не должен оплачивать это.

Другими словами, поскольку перемещение мьютекса не является основной операцией того, что представляет собой мьютекс, не требуя перемещения std::mutex, не удаляет никаких функциональных возможностей (благодаря не подвижным T = > перемещаемое преобразование std::unique_ptr<T>) и будет нести минимальные накладные расходы за счет использования собственного типа.


std::thread мог быть аналогичным образом задан, чтобы не быть подвижным, что сделало бы типичное время жизни как таковое: выполнение (связанное с потоком выполнения) после вызова конструктора с оценкой; и отсоединены/объединены (не связаны нитями исполнения), после вызова join или detach. Насколько я понимаю, std::vector<std::thread> все равно был бы полезен, так как тип был бы EmplaceConstructible.

Изменить: Неверно! Тип все равно должен быть подвижным (при перераспределении в конце концов). Для меня это достаточно обоснованное: типично помещать std::thread в контейнеры, такие как std::vector и std::deque, поэтому функциональность приветствуется для этого типа.