C++ 的 std::unique_ptr
有个神奇的特性:如果使用默认的 deleter(即使用 operator delete
),或者 non-capturing lambda 作为 deleter,则有
1
| sizeof(std::unique<T>) == sizeof(void*);
|
即整个对象的内存布局和 trivial pointer 一致,没有额外的开销。
这个特性的背后就是 compress-pair;这个设施能够在某个元素是一个 empty class 时避免为其分配内存。
注:这里假设你知道什么是 EBO,以及为什么会有 EBO。
这里自己动手实现一个 compressed pair:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| template<typename Tx, typename Ty, bool = std::is_empty<Tx>::value> struct CompressedPair : Tx { Ty second;
template<typename T> explicit CompressedPair(T&& t) : Tx(), second(std::forward<T>(t)) {}
template<typename T, typename U> CompressedPair(T&& t, U&& u) : Tx(std::forward<T>(t)), second(std::forward<U>(u)) {}
Tx& get_first() noexcept { return *this; }
const Tx& get_first() const noexcept { return *this; } };
template<typename Tx, typename Ty> struct CompressedPair<Tx, Ty, false> { Tx first; Ty second;
template<typename T, typename U> CompressedPair(T&& t, U&& u) : first(std::forward<T>(t)), second(std::forward<U>(u)) {}
Tx& get_first() noexcept { return first; }
const Tx& get_first() const noexcept { return first; } };
|
因为 EBO 是实现的核心,而父类的构造顺序先于子类的任何成员,上面将 Tx
作为可被优化的成员。