我可以空基优化可变数据吗?
Can I empty-base optimize mutable data?
我有一个 class 模板,如下所示:
template<typename T, typename Mutex, typename SomePolicy>
class my_class {
public:
T f() const {
resource_lock a_lock(some_mutex);
return some_policy.some_operation(some_data);
}
private:
T some_data;
mutable Mutex some_mutex;
SomePolicy some_policy;
};
如果不同时使用,我们有一个虚拟互斥锁类型,它的所有成员函数都是内联的空函数,没有数据。有些策略具有每个实例数据,有些策略没有任何数据。
这是库代码,事实证明这个 class 模板被用在应用程序代码中,其中额外的字节很重要,数据成员 some_mutex
和 some_policy
甚至当它们为空时 classes。所以我想利用空基优化。对于政策,这很简单:
template<typename T, typename Mutex, typename SomePolicy>
class my_class {
public:
T f() const {
resource_lock a_lock(the_data.some_mutex);
return the_data.some_operation(the_data.some_data);
}
private:
struct data : SomePolicy {
T some_data;
mutable Mutex some_mutex;
};
data the_data;
};
但是,鉴于 some_mutex
是 mutable
,我不知道如何在不制作 the_data
的情况下将其作为基础 class,因此所有数据, mutable
,从而完全接管了编译器保护我免受愚蠢的常量错误的责任。
有没有办法将 mutable
数据成员转换为非可变数据成员的 class 的基数?
不,碱基 class 不能是 mutable
。但是...
thereby completely taking over the compiler's responsibility to protect me from silly constness mistakes.
...不一定是这样的结果。您仍然可以让编译器帮助您,方法是创建访问函数而不是直接使用您的数据结构。你可以用这样的方式命名它,让每个人都清楚这些访问函数是唯一受支持的数据接口。
mutable struct : SomePolicy, Mutex {
T some_data;
} _dont_use_directly;
T &some_data() { return _dont_use_directly.some_data; }
const T &some_data() const { return _dont_use_directly.some_data; }
SomePolicy &some_policy() { return _dont_use_directly; }
const SomePolicy &some_policy() const { return _dont_use_directly; }
Mutex &some_mutex() const { return _dont_use_directly; }
您可以做的是使用互斥锁包装器并将其专门用于空互斥锁,然后您可以对其执行 EBCO。
class EmptyMutex{
void lock() const {};
void unlock() const {};
};
template< class MUX>
class MutexWrapper {
mutable MUX mux;
public:
void lock() const {mux.lock();};
void unlock() const { mux.unlock() ;};
};
template<>
class MutexWrapper<EmptyMutex> : public EmptyMutex {};
template<typename T, typename Mutex, typename SomePolicy>
class my_class {
public:
T f() const {
resource_lock a_lock(the_data);
return the_data.some_operation(the_data.some_data);
}
private:
struct data : SomePolicy ,MutexWrapper<Mutex> {
T some_data;
};
data the_data;
};
此解决方案的警告是,在 const 成员函数内 - 虽然您可以直接使用 lock() 和 unlock() 函数,但您只能将 const 引用作为参数传递给 MutexWrapper。
因此,在这种情况下,您的 resource_lock 必须采用 const 对 MutexWrapper 的引用 - 当人们期望(并且正确地)它实际上改变了互斥锁的状态时.对于不知道 MutexWrapper
是如何实现的人来说,这是相当误导的。
出于这个原因,我认为在需要时只 const_cast 互斥体比使用包装器更明智:
template<typename T, typename Mutex, typename SomePolicy>
class my_class {
public:
T f() const {
resource_lock a_lock(getNonConstMuxRef());
return the_data.some_operation(the_data.some_data);
}
private:
struct data : SomePolicy, Mutex {
T some_data;
};
data the_data;
Mutex& getNonConstMuxRef() const { return const_cast<my_class<T, Mutex, SomePolicy>*>(this)->the_data; }
};
假设您的 std::tuple
实现了空碱基优化(进行检查),那么这可能会有所帮助:
mutable std::tuple<T, Mutex, SomePolicy> raw;
T const& data() const { return std::get<0>(raw); }
T & data() { return std::get<0>(raw); }
Mutex & mutex() const { return std::get<1>(raw); }
SomePolicy const& policy() const { return std::get<2>(raw); }
SomePolicy & policy() { return std::get<2>(raw); }
基本上我们将优化放入我们从未访问过的 .raw
mutable
成员中(作为奖励,元组的访问是混乱的)。然后我们创建强制执行 const
.
的引用访问器
您可能还想:
my_class(my_class const& )=default;
my_class(my_class && )=default;
my_class&operator=(my_class const& )=default;
my_class&operator=(my_class && )=default;
明确表示 my_class const&&
不在游戏中。这还假设 T
和其他类型具有行为良好的复制 ctors 等。 (例如,他们没有 T(T&)
ctor 或 operator=
感觉对 rhs 的非 const
-ness 有过分的权利)
我有一个 class 模板,如下所示:
template<typename T, typename Mutex, typename SomePolicy>
class my_class {
public:
T f() const {
resource_lock a_lock(some_mutex);
return some_policy.some_operation(some_data);
}
private:
T some_data;
mutable Mutex some_mutex;
SomePolicy some_policy;
};
如果不同时使用,我们有一个虚拟互斥锁类型,它的所有成员函数都是内联的空函数,没有数据。有些策略具有每个实例数据,有些策略没有任何数据。
这是库代码,事实证明这个 class 模板被用在应用程序代码中,其中额外的字节很重要,数据成员 some_mutex
和 some_policy
甚至当它们为空时 classes。所以我想利用空基优化。对于政策,这很简单:
template<typename T, typename Mutex, typename SomePolicy>
class my_class {
public:
T f() const {
resource_lock a_lock(the_data.some_mutex);
return the_data.some_operation(the_data.some_data);
}
private:
struct data : SomePolicy {
T some_data;
mutable Mutex some_mutex;
};
data the_data;
};
但是,鉴于 some_mutex
是 mutable
,我不知道如何在不制作 the_data
的情况下将其作为基础 class,因此所有数据, mutable
,从而完全接管了编译器保护我免受愚蠢的常量错误的责任。
有没有办法将 mutable
数据成员转换为非可变数据成员的 class 的基数?
不,碱基 class 不能是 mutable
。但是...
thereby completely taking over the compiler's responsibility to protect me from silly constness mistakes.
...不一定是这样的结果。您仍然可以让编译器帮助您,方法是创建访问函数而不是直接使用您的数据结构。你可以用这样的方式命名它,让每个人都清楚这些访问函数是唯一受支持的数据接口。
mutable struct : SomePolicy, Mutex {
T some_data;
} _dont_use_directly;
T &some_data() { return _dont_use_directly.some_data; }
const T &some_data() const { return _dont_use_directly.some_data; }
SomePolicy &some_policy() { return _dont_use_directly; }
const SomePolicy &some_policy() const { return _dont_use_directly; }
Mutex &some_mutex() const { return _dont_use_directly; }
您可以做的是使用互斥锁包装器并将其专门用于空互斥锁,然后您可以对其执行 EBCO。
class EmptyMutex{
void lock() const {};
void unlock() const {};
};
template< class MUX>
class MutexWrapper {
mutable MUX mux;
public:
void lock() const {mux.lock();};
void unlock() const { mux.unlock() ;};
};
template<>
class MutexWrapper<EmptyMutex> : public EmptyMutex {};
template<typename T, typename Mutex, typename SomePolicy>
class my_class {
public:
T f() const {
resource_lock a_lock(the_data);
return the_data.some_operation(the_data.some_data);
}
private:
struct data : SomePolicy ,MutexWrapper<Mutex> {
T some_data;
};
data the_data;
};
此解决方案的警告是,在 const 成员函数内 - 虽然您可以直接使用 lock() 和 unlock() 函数,但您只能将 const 引用作为参数传递给 MutexWrapper。
因此,在这种情况下,您的 resource_lock 必须采用 const 对 MutexWrapper 的引用 - 当人们期望(并且正确地)它实际上改变了互斥锁的状态时.对于不知道 MutexWrapper
是如何实现的人来说,这是相当误导的。
出于这个原因,我认为在需要时只 const_cast 互斥体比使用包装器更明智:
template<typename T, typename Mutex, typename SomePolicy>
class my_class {
public:
T f() const {
resource_lock a_lock(getNonConstMuxRef());
return the_data.some_operation(the_data.some_data);
}
private:
struct data : SomePolicy, Mutex {
T some_data;
};
data the_data;
Mutex& getNonConstMuxRef() const { return const_cast<my_class<T, Mutex, SomePolicy>*>(this)->the_data; }
};
假设您的 std::tuple
实现了空碱基优化(进行检查),那么这可能会有所帮助:
mutable std::tuple<T, Mutex, SomePolicy> raw;
T const& data() const { return std::get<0>(raw); }
T & data() { return std::get<0>(raw); }
Mutex & mutex() const { return std::get<1>(raw); }
SomePolicy const& policy() const { return std::get<2>(raw); }
SomePolicy & policy() { return std::get<2>(raw); }
基本上我们将优化放入我们从未访问过的 .raw
mutable
成员中(作为奖励,元组的访问是混乱的)。然后我们创建强制执行 const
.
您可能还想:
my_class(my_class const& )=default;
my_class(my_class && )=default;
my_class&operator=(my_class const& )=default;
my_class&operator=(my_class && )=default;
明确表示 my_class const&&
不在游戏中。这还假设 T
和其他类型具有行为良好的复制 ctors 等。 (例如,他们没有 T(T&)
ctor 或 operator=
感觉对 rhs 的非 const
-ness 有过分的权利)