Вопрос или проблема
У меня есть класс с мьютексом:
Class StoreNumbers{
StoreNumbers();
AddNumber(size_t number){
const std::lock_guard<std::mutex> lock(lock_);
numbers_.push_back(number);
}
private:
std::mutex lock_;
std::vector<size_t> numbers_;
};
Цель класса — возможность хранить числа при использовании в многопоточном контексте. Несомненно, есть лучшие способы сделать это, но это иллюстрирует мой вопрос.
Теперь я хочу иметь различные объекты этих классов, чтобы хранить разные типы чисел. Я также хочу, чтобы каждый из этих типов имел свой собственный мьютекс, так как они не связаны между собой.
Если я пишу:
std::vector<StoreNumbers> vec;
vec.push_back(StoreNumber());
Я получаю ошибку компилятора: no matching function for call to ‘construct_at’, что я понимаю, поскольку мьютекс нельзя копировать.
Если вместо этого я использую:
std::unique_ptr<std::mutex> lock_=std::make_unique<std::mutex>();
для блокировки и разыменовываю его для lock_guard, я больше не получаю ошибку. Я не понимаю, почему. Спасибо за помощь!
Ответ или решение
Ошибка "no matching function for call to ‘construct_at’" возникает в вашем коде из-за попытки копирования нестандартных объектов, таких как std::mutex
, в стандартные контейнеры, как std::vector
.
Когда вы пытаетесь создать вектор объектов типа StoreNumbers
, компилятор пытается создать временные копии, чтобы разместить их в векторе. Однако, std::mutex
не поддерживает операцию копирования, ибо это приводит к неопределённому поведению в многопоточном окружении, что было подсознательно задумано разработчиками библиотеки C++. Таким образом, когда вы вызываете vec.push_back(StoreNumber());
, происходит попытка копии объекта, содержащего std::mutex
, что и вызывает ошибку.
Теперь рассмотрим, почему использование std::unique_ptr<std::mutex>
исправляет эту ситуацию. При замене std::mutex
на std::unique_ptr<std::mutex>
в вашем классе StoreNumbers
, вы меняете стратегию управления ресурсами. std::unique_ptr
позволяет динамически выделять память для std::mutex
, управляя её жизненным циклом. Эта конструкция является копируемой вектором, так как std::unique_ptr
поддерживает перемещение, а не копирование.
Когда вы делаете следующее:
std::unique_ptr<std::mutex> lock_ = std::make_unique<std::mutex>();
Вы обеспечиваете создание нового объекта std::mutex
в динамической памяти, который управляется указателем. При этом, при операции добавления в вектор, вместо копирования объекта, происходит перемещение указателя на std::mutex
. То есть, место в памяти, в котором находится std::mutex
, не копируется, а передаётся управление. Таким образом, каждая новая инстанция StoreNumbers
, хранится в векторе с уникальным, отдельно выделенным mutex. Это предотвращает любые проблемы, связанные с копированием, и устраняет возникшую ранее ошибку компиляции.
Этот подход не только исправляет ошибку, но и повышает гибкость и безопасность вашего многопоточного кода. Поскольку каждый StoreNumbers
теперь имеет свой собственный экземпляр std::mutex
, код становится более безопасным и эффективным для параллельного выполнения.
Такое управление ресурсами и интеграция уникальных указателей является стандартом хорошей практики в современном C++. Это помогает избегать ошибок и утечек памяти, обеспечивая более безопасный и чистый код, особенно в многоуровневом программировании.
Таким образом, использование std::unique_ptr
не только устраняет ошибку компиляции, но и улучшает архитектуру вашего приложения, сделав его более безопасным и понятным.