在Meeting C++ 2019上,Jon Kalb讨论了模板技术并提到了策略类。参见此处,了解来源:https://youtu.be/MLV4IVc4SwI?t=1815

有问题的有趣代码段是:

template<class T, class CheckingPolicy>
struct MyContainer : private CheckingPolicy
{
    ...
}

我经常看到这种类型的设计,我想知道这里的继承相对于组合是否具有真正的优势。从我的个人经验中,我已经了解了很多有关继承模式下的偏好组合的知识。因此,我编写代码的方式将更像这样:
template<class T, class CheckingPolicy>
struct MyContainer
{
    CheckingPolicy policy;
    ...
}

不会涉及任何虚拟功能。不过,如果您能分享一些不同之处的见解,我将不胜感激。我对内存布局及其影响方面的差异特别感兴趣。如果CheckingPolicy没有数据成员,而只有check方法或重载的调用运算符,会有所不同吗?

最佳答案

可能的原因之一:当您从CheckingPolicy继承时,您可以从empty base class optimization中受益。

如果CheckingPolicyempty(即,除了大小为0的位字段之外,没有其他非静态数据成员,没有虚拟函数,没有虚拟基类和非空基类),则它将不影响MyContainer的大小。

相反,当它是MyContainer的数据成员时,即使CheckingPolicy为空,但MyContainer的大小也会增加至少一个字节。至少,由于对齐要求,您可能需要其他填充字节。

这就是为什么在例如std::vector的实现中可以从分配器中找到特性的原因。例如,libstdc++的implementation:

template<typename _Tp, typename _Alloc>
struct _Vector_base {
    typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
        rebind<_Tp>::other _Tp_alloc_type;

    struct _Vector_impl : public _Tp_alloc_type, public _Vector_impl_data {
        // ...
    };

    // ...
};

无状态分配器(如没有非静态数据成员的CheckingPolicy)不会影响std::vector的大小。

在C++ 20中,我们将使用 [[no_unique_address]] 来潜在地解决此问题:尽管标准布局类型需要空基础优化,但是[[no_unique_address]]只是许可,而不是要求。 (感谢 Nicol Bolas 指出了这一点。)

关于基于C++策略的设计: Inheritance vs composition,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59502951/

10-09 19:20