空基类优化(EBCO,Empty Base Class Optimization)
为了保证给类动态分配内存时有不同的地址,C++ 规定空类大小必须大于 0
namespace jc {
struct A {};
struct B {};
static_assert (sizeof (A) > 0 );
static_assert (sizeof (B) > 0 );
} // namespace jc
int main () {
jc::A a;
jc::B b;
static_assert ((void *)&a != (void *)&b);
}
一般编译器将空类大小设为 1 字节,对于空类存在继承关系的情况,如果支持 EBCO,可以优化派生类的空间占用大小
/* 不支持 EBCO 的内存布局:
* [ ] } A } B } C
* [ ] } }
* [ ] }
*
* 支持 EBCO 的内存布局:
* [ ] } A } B } C
*/
namespace jc {
struct A {
using Int = int ;
};
struct B : A {};
struct C : B {};
static_assert (sizeof (A) == 1 );
static_assert (sizeof (A) == sizeof (B));
static_assert (sizeof (A) == sizeof (C));
} // namespace jc
int main () {}
namespace jc {
struct A {};
struct B {};
template <typename T, typename U>
struct C {
T a;
U b;
};
static_assert (sizeof (C<A, B>) == 2 );
} // namespace jc
int main () {}
为了利用 EBCO 压缩内存空间,可以将模板参数设为基类
namespace jc {
struct A {};
struct B {};
template <typename T, typename U>
struct C : T, U {};
static_assert (sizeof (C<A, B>) == 1 );
} // namespace jc
int main () {}
但模板参数可能是相同类型,或者不一定是类,此时将其设为基类在实例化时会报错。如果已知一个模板参数类型为空类,把可能为空的类型参数与一个不为空的成员利用 EBCO 合并起来,即可把空类占用的空间优化掉
namespace jc {
template <typename Base, typename Member>
class Pair : private Base {
public:
Pair (const Base& b, const Member& m) : Base(b), member_(m) {}
const Base& first () const { return (const Base&)*this ; }
Base& first () { return (Base&)*this ; }
const Member& second () const { return this ->member_ ; }
Member& second () { return this ->member_ ; }
private:
Member member_;
};
template <typename T>
struct Unoptimizable {
T info;
void * storage;
};
template <typename T>
struct Optimizable {
Pair<T, void *> info_and_storage;
};
} // namespace jc
struct A {};
static_assert (sizeof (jc::Unoptimizable<A>) == 2 * sizeof(void *));
static_assert (sizeof (jc::Optimizable<A>) == sizeof(void *));
int main () {}
奇异递归模板模式(CRTP,The Curiously Recurring Template Pattern)
#include < cassert>
namespace jc {
template <typename T>
class Base {
public:
static int count () { return i; }
protected:
Base () { ++i; }
Base (const Base<T> &) { ++i; }
Base (Base<T> &&) noexcept { ++i; }
~Base () { --i; }
private:
inline static int i = 0 ;
};
template <typename T>
class Derived : public Base <Derived<T>> {};
} // namespace jc
int main () {
jc::Derived<int > a, b;
jc::Derived<char > c;
assert (jc::Derived<int >::count () == 2 );
assert (jc::Derived<char >::count () == 1 );
}
通常大量运算符重载会一起出现,但通常这些运算符只需要一个定义,其他运算符可以提取到基类中基于这一个来实现
#include < cassert>
namespace jc {
template <typename T>
class Base {
friend bool operator !=(const T& lhs, const T& rhs) { return !(lhs == rhs); }
};
class Derived : public Base <Derived> {
friend bool operator ==(const Derived& lhs, const Derived& rhs) {
return lhs.i_ == rhs.i_ ;
}
public:
Derived (int i) : i_(i) {}
private:
int i_ = 0 ;
};
} // namespace jc
int main () {
jc::Derived a{1 };
jc::Derived b{2 };
assert (a != b);
}
CRTP 基类可以基于 CRTP 派生类暴露的小得多的接口定义大部分接口,这个模式称为 facade 模式
#include < cassert>
#include < iterator>
#include < type_traits>
#include < vector>
namespace jc {
template <typename Derived, typename Value, typename Category>
class IteratorFacade {
public:
using value_type = std::remove_const_t <Value>;
using reference = Value&;
using pointer = Value*;
using difference_type = std::ptrdiff_t ;
using iterator_category = Category;
public:
reference operator *() const { return as_derived ().dereference (); }
Derived& operator ++() {
as_derived ().increment ();
return as_derived ();
}
Derived operator ++(int ) {
Derived tmp (as_derived ());
as_derived ().increment ();
return tmp;
}
friend bool operator ==(const IteratorFacade& lhs, const IteratorFacade& rhs) {
return lhs.as_derived ().equals (rhs.as_derived ());
}
friend bool operator !=(const IteratorFacade& lhs, const IteratorFacade& rhs) {
return !operator ==(lhs, rhs);
}
private:
Derived& as_derived () { return *static_cast <Derived*>(this ); }
const Derived& as_derived () const {
return *static_cast <const Derived*>(this );
}
};
template <typename T>
struct ListNode {
ListNode (T x) : value(x) {}
T value;
ListNode<T>* next = nullptr ;
};
template <typename T>
class ListNodeIterator
: public IteratorFacade<ListNodeIterator<T>, T, std::forward_iterator_tag> {
public:
ListNodeIterator (ListNode<T>* t = nullptr ) : t_(t) {}
T& dereference () const { return t_->value ; }
void increment () { t_ = t_->next ; }
bool equals (const ListNodeIterator& rhs) const { return t_ == rhs.t_ ; }
private:
ListNode<T>* t_ = nullptr ;
};
} // namespace jc
int main () {
auto a = new jc::ListNode<int >{1 };
auto b = new jc::ListNode<int >{2 };
auto c = new jc::ListNode<int >{3 };
a->next = b;
b->next = c;
auto first = jc::ListNodeIterator{a};
auto last = ++jc::ListNodeIterator{c};
std::vector<int > v;
for (auto it = first; it != last; ++it) {
v.emplace_back (*it);
}
assert ((v == std::vector<int >{1 , 2 , 3 }));
delete c;
delete b;
delete a;
}
#include < cassert>
#include < string>
namespace jc {
template <typename ... Mixins>
struct Point : Mixins... {
Point () : Mixins()..., x(0.0 ), y(0.0 ) {}
Point (double x, double y) : Mixins()..., x(x), y(y) {}
double x;
double y;
};
struct Label {
std::string label = " point" ;
};
struct Color {
enum { red, green, blue };
};
using CustomPoint = Point <Label, Color>;
} // namespace jc
int main () {
jc::CustomPoint p;
assert (p.label == " point" );
assert (p.red == jc::Color::red);
assert (p.green == jc::Color::green);
assert (p.blue == jc::Color::blue);
}
#include < cassert>
#include < string>
namespace jc {
template <typename T>
class Base {
public:
static int count () { return i; }
protected:
Base () { ++i; }
Base (const Base<T> &) { ++i; }
Base (Base<T> &&) noexcept { ++i; }
~Base () { --i; }
private:
inline static int i = 0 ;
};
template <template <typename > class ... Mixins>
struct Point : Mixins<Point <>>... {
Point () : Mixins<Point <>>()..., x(0.0 ), y(0.0 ) {}
Point (double x, double y) : Mixins<Point <>>()..., x(x), y(y) {}
double x;
double y;
};
template <typename T>
struct Label {
std::string label = " point" ;
};
template <typename T>
struct Color {
enum { red, green, blue };
};
using PointCount = Point <Base, Label, Color>;
} // namespace jc
int main () {
jc::PointCount a, b, c;
assert (jc::PointCount::count () == 3 );
assert (a.label == " point" );
assert (a.red == jc::Color<void >::red);
assert (a.green == jc::Color<void >::green);
assert (a.blue == jc::Color<void >::blue);
}
#include < cassert>
namespace jc {
template <typename ... Mixins>
class Base : private Mixins ... {
public:
int f () { return 1 ; } // 是否为虚函数由 Mixins 中的声明决定
};
template <typename ... Mixins>
class Derived : public Base <Mixins...> {
public:
int f () { return 2 ; }
};
} // namespace jc
struct A {};
struct B {
virtual int f () = 0;
};
int main () {
jc::Base<A>* p = new jc::Derived<A>;
assert (p->f () == 1 );
jc::Base<B>* q = new jc::Derived<B>;
assert (q->f () == 2 );
}
struct A {};
struct B {};
struct C {};
template <typename T1 = A, typename T2 = B, typename T3 = C>
struct MyClass {};
namespace jc {
struct A {};
struct B {};
struct C {
static constexpr int f () { return 1 ; }
};
struct Alias {
using P1 = A;
using P2 = B;
using P3 = C;
};
template <typename T>
struct SetT1 : virtual Alias {
using P1 = T;
};
template <typename T>
struct SetT2 : virtual Alias {
using P2 = T;
};
template <typename T>
struct SetT3 : virtual Alias {
using P3 = T;
};
// 由于不能从多个相同类直接继承,需要一个中间层用于区分
template <typename T, int N>
struct Mid : T {};
template <typename T1, typename T2, typename T3>
struct SetBase : Mid<T1, 1 >, Mid<T2, 2 >, Mid<T3, 3 > {};
/* Alias 要被用作默认实参
* 但 SetBase 会将其多次指定为 Mid 的基类
* 为了防止多次继承产生二义性
* 虚派生一个新类替代 Alias 作为默认实参
*/
struct Args : virtual Alias {}; // Args 即包含了别名 P1、P2、P3
template <typename T1 = Args, typename T2 = Args, typename T3 = Args>
struct MyClass {
using Policies = SetBase<T1, T2, T3>;
constexpr int f () { return Policies::P3::f (); }
};
struct D {
static constexpr int f () { return 2 ; }
};
static_assert (MyClass{}.f() == 1 );
static_assert (MyClass<SetT3<D>>{}.f() == 2 );
} // namespace jc
int main () {}