Помните, что C ++ принимает философию «не плати за то, что не используешь». Давайте рассмотрим, например, воображаемую платформу, которая использует непрозрачный тип для представления мьютекса; давайте назовем этот тип 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
, поэтому функциональность этого типа приветствуется.