我可以空基优化可变数据吗?

sbi*_*sbi 13 c++ optimization mutable

我有一个类模板,看起来像这样:

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;
};
Run Code Online (Sandbox Code Playgroud)

如果不同时使用,我们有一个虚拟互斥类型,它具有所有成员函数作为内联空函数而没有数据.有些策略包含每个实例数据和那些没有任何数据的策略.

这是库代码,事实证明这个类模板在应用程序代码中使用,其中额外的字节对于数据成员是必需的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;
};
Run Code Online (Sandbox Code Playgroud)

但是,考虑到这some_mutex一点mutable,我不知道如何使它成为一个没有制作的基类the_data,因此所有数据mutable,从而完全接管编译器的责任,保护我免受愚蠢的常量错误.

有没有办法将mutable数据成员转变为非可变数据成员类的基础?

小智 7

不,基类不能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; }
Run Code Online (Sandbox Code Playgroud)


Mik*_*eMB 5

您可以做的是使用互斥包装器并将其专门用于空的互斥锁,然后您可以执行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;
};
Run Code Online (Sandbox Code Playgroud)

这个解决方案的注意事项是 - 在const成员函数内 - 虽然你可以直接使用lock()和unlock()函数,但你只能将const引用作为参数传递给MutexWrapper.所以在这种情况下,你的resource_lock必须对一个MutexWrapper 进行const引用 - 当人们期望(并且正确地)它实际上改变了互斥体的状态时.对于不知道如何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; }
};
Run Code Online (Sandbox Code Playgroud)