C++ 的 idioms(十)

compile-time control structure

通过 template 我们可以写 if then else,甚至循环。

template <int begin, int end, ...>
struct loop {
} ;
template <int end, ...>
struct loop <end, end, ...> {
} ;

通过以上方式我们可以写出 metafunction 多次作用的结果。

final class

羡慕 Java 程序员能写 final?

template <class T>
struct type_wrapper {
  typedef T type ;
} ;

template <class T>
class make_final {
  friend class type_wrapper<T>::type ;
  ~make_final () {}
} ;

class sealed : virtual make_final<sealed> {
} ;

这样一来试图继承你的 sealed 就会在编译的时候说试图调用析构不成。这里主要是利用了 friend class 能够调用对应的析构,但是由于是 virtual 继承,要求 most derived class (即继承树叶子)调用对应的构造与析构函数(正常的继承都是调用上一级父类的构造函数),在 make_final 中却仅仅认定 sealed 是 friend,这样 make_final 的私有析构函数仅能被 sealed 调用。这里不允许 template 的参数 T 作为 friend 的指向(据说 C++ 标准规定的?),所以通过一个无聊的 type_wrapper 绕开了这个限制。这是对 wikibooks 的例子的改进。

include guard macro

这个过于简单了,写头文件必备的 ifndef define endif,有人喜欢 pragma once,但是实际上前者更通用,而且现代的很多 preprocessor 都能认出前面这种 pattern 优化打开文件次数了。

inline guard macro

这主要是为了在 inline 与非 inline 之间快速切换。template 里面的 inline 一般不是很好处理,但是也可以使用这个 idiom。一般将 inline 实现写在一个 ipp 文件里面,通过一个外部宏,如 HAVE_INLINE 决定插入到 .cpp 还是 .h 中,下面是 wikibooks 的例子

// test.ipp file
INLINE void Test::func() {}

// test.hpp file
#ifndef __TEST_H
#define __TEST_H

class Test {
public:
  void func();
};

#ifdef HAVE_INLINE
#define INLINE inline
#include "test.ipp"
#endif
#endif  // __TEST_H

//test.cpp file
#include "test.hpp"

#ifndef HAVE_INLINE
#define INLINE
#include "test.ipp"
#endif

generic container

随便写一个 container 对数据类型的 requirement 其实还是比较多的,稍不留神,比如 default constructable,copy constructor,non-throwing destructor 和 copy-assignment operator 这些可能都会有要求。设计 generic container 的原则就是在此基础上进一步减少这些 requirement:最好只需要 copy constructor 与 non-throwing destructor。简而言之,就是只依赖于

// construct helper using placement new:
template <class T1, class T2>
void construct (T1 &p, const T2 &value) {
  new (&p) T1(value); // T must support copy-constructor
}

// destroy helper to invoke destructor explicitly.
template <class T>
void destroy (T const &t)  throw () {
  t.~T(); // T must support non-throwing destructor
}

符合这种要求的实现有 STL container。一般来说 STL 容器会维护一段内存,只在增加元素的时候通过 placement copy constructor 构造对象,减少元素的时候调用以上类似的析构函数。

Free function allocator

这是相对现行 STL allocator 的一种简化,它仅仅包括类似 malloc 和 free 的分配和释放。

struct user_allocator_nedmalloc {
  typedef std::size_t size_type;
  typedef std::ptrdiff_t difference_type;

  static inline char* malloc(const size_type bytes) {
    return reinterpret_cast<char*>(nedmalloc(bytes));
  }
  static inline void free(char* const block) {
    nedfree(block);
  }
};

这个文档里面写了诸多理由。大致的意思似乎是说现在的 allocator 一方面与容器耦合在一起了(vector 可能就是数据本身,但是 list 之类就得存链表节点)不方便不同容器之间的数据交换还是怎么,另一方面也不方便动态切换 allocator。

——————
And, behold, the word of the LORD came to him, saying, This shall not be your heir; but he that shall come forth out of your own bowels shall be your heir.

Advertisements
C++ 的 idioms(十)

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s