Пустые элементы данных можно оптимизировать, используя оптимизацию пустого базового класса (EBO). То есть, хотя класс без состояния имеет ненулевой размер (как минимум 1 байт), он не потребляет дополнительную память, если используется в качестве базового класса:
template <typename Mutex>
struct CompressedMutex
{
Mutex mutex;
Mutex& getMutex() { return mutex; }
};
template <>
struct CompressedMutex<NoMutex> : NoMutex
{
NoMutex& getMutex() { return *this; }
};
template <typename Mutex>
class MyClass : CompressedMutex<Mutex>
{
public:
void someFunction()
{
std::unique_lock<Mutex> l(this->getMutex());
}
};
DEMO
Обратите внимание, что this->
или CompressedMutex<Mutex>::
требуется при доступе к getMutex()
, которое является зависимым именем.
Однако обычно требуется заблокировать мьютекс и в const
-квалифицированных функциях-членах. Для этого ключевое слово mutable
можно использовать в определении элемента данных мьютекса. В случае NoMutex
вместо этого потребуется использовать const_cast<CompressedMutex&>(*this)
или объявить возвращаемый объект static
:
template <typename Mutex>
struct CompressedMutex
{
mutable Mutex mutex;
Mutex& getMutex() const { return mutex; }
};
template <>
struct CompressedMutex<NoMutex>
{
static NoMutex mutex;
NoMutex& getMutex() const { return mutex; }
};
Этот метод широко используется в стандартной библиотеке, поэтому распределитель без сохранения состояния (включая std::allocator<T>
) не учитывает общий размер контейнера. Такие объекты обычно хранятся в так называемой сжатой паре (например, boost::compressed_pair
) или ее вариации, иногда вместе с непустым элементом данных, так что интерфейс охватывающий класс не меняется:
#include <boost/compressed_pair.hpp>
template <typename Mutex>
class MyClass
{
public:
void someFunction()
{
std::unique_lock<Mutex> l(data.second());
}
private:
boost::compressed_pair<SomeDataMemberType, Mutex> data;
};