提问人:sbi 提问时间:2/24/2015 更新时间:2/28/2015 访问量:380
我可以空基地优化可变数据吗?
Can I empty-base optimize mutable data?
问:
我有一个类模板,看起来像这样:
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;
};
如果不同时使用,我们有一个虚拟互斥类型,它将所有成员函数都作为内联空函数,并且没有数据。有些策略具有每个实例的数据,有些策略没有任何数据。
这是库代码,事实证明,此类模板用于应用程序代码中,其中数据成员需要额外的字节,即使它们是空类时也是如此。所以我想利用空碱优化。对于策略,这很简单:some_mutex
some_policy
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
mutable
有没有办法将可变
数据成员转换为不可可变数据成员类的基础?
答:
不可以,基类不能是 .但。。。mutable
从而完全接管了编译器的责任,以保护我免受愚蠢的恒常性错误的影响。
...这不一定是结果。您仍然可以通过创建访问器函数而不是直接使用数据结构来让编译器为您提供帮助。你可以用这样一种方式命名它,即每个人都应该清楚地知道,这些访问器函数是唯一受支持的数据接口。
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; }
评论
:)
// Use inheritance instead of multiple members to reduce class size when instantiated with empty dummy policy and/or mutex. Mutex is supposed to be mutable, unfortunately this means all data is technically mutable.
您可以做的是使用互斥锁包装器,并将其专门用于空互斥锁,然后您可以对其执行 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必须对互斥锁包装器进行常量引用 - 当人们期望(并且正确地)它实际上更改了互斥锁的状态时。对于不知道如何实现的人来说,这是非常错误的。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; }
};
评论
MutexWrapper
lock()
unlock()
const
假设你实现了空基础优化(做检查),那么这可能会有所帮助: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;
明确地说,这不在起作用。这也假设和其他类型具有表现良好的复制 ctor 等。(例如,他们没有 CTOR,或者对 RHS 的非性感到过于有权)my_class const&&
T
T(T&)
operator=
const
评论
mutable_if_nonempty<T>
T& get() const;
T
mutable T t;
T const& get() const;
T
const_cast<T&>(*this)
T
T& get() const;
T