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

Can I empty-base optimize mutable data?

提问人:sbi 提问时间:2/24/2015 更新时间:2/28/2015 访问量:380

问:

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

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_mutexsome_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_mutexmutablethe_datamutable

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

C++ 优化 可变

评论

0赞 Yakk - Adam Nevraumont 2/24/2015
元组和 5 个引用访问器?
0赞 dyp 2/24/2015
您可以编写一个包装类模板,该模板提供成员函数 IFF 为非空(通过存储 ),否则提供成员函数(通过继承自 )。或者,我认为如果 a 为空,则 a 是安全的,允许在继承时实现 a。mutable_if_nonempty<T>T& get() const;Tmutable T t;T const& get() const;Tconst_cast<T&>(*this)TT& get() const;T

答:

7赞 user743382 2/24/2015 #1

不可以,基类不能是 .但。。。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; }

评论

0赞 sbi 2/24/2015
这里有两个对聪明的赞成票,一个对混淆的反对票。 (我认为这会起作用,但它有点笨拙,并且需要向任何维护者解释一个大小适中的注释。:)
0赞 2/24/2015
@sbi呵呵,是的,评论是合适的。但是像“”这样的简单词就足够清楚了,不是吗?// 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.
5赞 MikeMB 2/24/2015 #2

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

评论

0赞 sbi 2/24/2015
(还有不止一个虚拟互斥锁类,但有模板元魔术可以在编译时检测到它们,所以我仍然可以专攻。MutexWrapper
0赞 MikeMB 2/24/2015
@sbi:好吧,你可以const_cast这个指针(我会把它包装在一个成员函数中),但这越来越难看。
0赞 MikeMB 2/24/2015
@sbi:实际上,我刚刚意识到,如果你必须在某个时候进行强制转换,那么整个包装器的想法或多或少是无用的(参见我的上一次编辑)
0赞 sbi 2/27/2015
我最终使用了这个变体,在那里我制作了虚拟互斥锁的 / 函数。这也需要更多的模板魔术才能在锁类中出现,但我现在让它工作了。既然你最接近我最后所做的事情,我就接受这个答案。lock()unlock()const
0赞 MikeMB 2/27/2015
@sbi:谢谢,如果它不涉及太多代码,你能展示一下你的最终解决方案到底是什么样子的吗?
4赞 Yakk - Adam Nevraumont 2/24/2015 #3

假设你实现了空基础优化(做检查),那么这可能会有所帮助: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); }

基本上,我们将优化放入一个我们从未访问过的成员中(作为奖励,元组的访问是混乱的)。然后,我们创建强制执行 ..rawmutableconst

您可能还需要:

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&&TT(T&)operator=const