C++ 的 idioms(十二)

move constructor

C++11x 新增的一个所谓 rvalue reference 以及所谓 move constructor 就是来自 boost.move 的一些相关工作。当然在没有这个东西之前 std::auto_ptr 其实也够用,只是需要注意的是函数返回的临时 auto_ptr 是 const 的,会出现前面 const auto_ptr 类似的问题,但是实际上很多编译器似乎没理会这件事情,这被认为是 unsafe 的。

std::auto_ptr<int>
func_auto_ptr (int d) {
  return std::auto_ptr<int> (new int (d)) ;
}

{
  std::auto_ptr<int> e (func_auto_ptr (4)) ;
}

一种解决这个问题的策略的想法是通过一个 proxy 类自动的临时的获得 ownership,然后这时去掉函数内部构造的 smart pointer 的 ownership,然后自动的转换成为函数返回值,这仍然是 const 对象,在后面进行 copy construct 的时候(我们不提供 copy construct,这会迫使编译器只能使用 move construct)不成,只能通过 proxy 重新来做这件事情。

template <class T>
struct proxy {
  T* t ;

  proxy(T* a) : t(a) {
    std::cout << "proxy()" << std::endl ;
  }

  ~proxy () {
    std::cout << "~proxy()" << std::endl ;
  }
} ;

template <class T>
struct to_cast {
  T* t ;

  explicit to_cast (T* a) : t(a) {
    std::cout << "constructor from normal pointer" << std::endl ;
  }

  to_cast (to_cast& that) {
    t = that.t ;
    that.t = NULL ;
    std::cout << "move constructor" << std::endl ;
  }

  to_cast (proxy<T> that) : t (that.t) {
    std::cout << "convert from proxy" << std::endl ;
  }

  ~to_cast () {
    delete t ;
    std::cout << "destructor" << std::endl ;
  }

  operator proxy<T> () {
    proxy<T> r (t) ;
    t = NULL ;
    std::cout << "convert to proxy" << std::endl ;
    return r ;
  }
} ;

这样可以保证在类似上面的调用情况下没有语义上的问题。

to_cast<int>
func (int d) {
  return to_cast<int> (new int (d)) ;
}

{
  to_cast<int> d (func (3)) ;
}

运行结果如下

constructor from normal pointer
proxy()
convert to proxy
convert from proxy
~proxy()
destructor
proxy()
convert to proxy
convert from proxy
~proxy()
destructor
destructor

multi-statement macro

需要写多行宏的时候常会因为最后用户多加一个 trailing semicolon 而出现错误,比较好的方式是把宏仍然伪装成类似某个语句的样子,常见的伪装方式如下

#define MACRO(arg1, arg2) do {  \
  /* declarations, if any */    \
  statement1;                   \
  statement2;                   \
  /* ... */                     \
  } while(0)    /* (no trailing ; ) */

通过这个 while (0) 和最后的分号连接,编译器一般能够优化处理掉这种没有循环的循环。

member detector

这是 SFINAE (替换失败并不是编译错误)的应用之一,检测某个类是否含有某些成员、成员函数,或者存在静态函数等。

#define CREATE_MEM_DETECTOR(x)             \
template<typename T> \
class detect_##x { \
  struct fallback { int x; } ; \
  struct derived : public T, public fallback { } ; \
  template<typename U, U> struct check ; \
  typedef char no  [1]; \
  typedef char yes [2]; \
  template<typename U> \
  static no  & func (check<int fallback::*, &U::x> *); \
  template<typename U> \
  static yes & func(...); \
public: \
  typedef detect_##x type; \
  enum { value = sizeof (func<derived> (0)) == sizeof (yes) }; \
}

struct foo {
  int y ;
} ;

CREATE_MEM_DETECTOR (x) ;
CREATE_MEM_DETECTOR (y) ;
// usee it like this
if (detect_x<foo>::value) {
  // ...
}

named constructor

这个其实相对容易,我们可以通过不同名字的 factory function 来产生对应的 object。对应的函数都是 static 的。具体的例子见这里

named parameter

这方面的工作怕是就是 boost.parameter 的“专利”了,能搞得跟 python 一样写函数,虽然一方面方便了用函数的人,也在另外的一方面损害了写函数的人 -,-。比起其他的 idiom 这个算是非常 ugly 的东西了吧,不到万不得已不要用…

named template parameter

这个技术是为了方便写多个模板参数。这部分内容来自这里。核心思想在于多个 policy 进行组合的时候,可以用继承的时候对父类 typedef 进行 override,这样获得的子类同样的 typedef 就能拿到不同的 policy 了。

struct default_policies {
  typedef default_quack_policy quack_policy ;
  typedef default_fly_policy fly_policy ;
  // many more policies ...
} ;

template <Policy>
struct set_quack : virtual public default_policies {
  typedef Policy quack_policy ;
} ;
// other policy setter...

似乎这样一来,我们就可以写个下面的东西

template <class setter1, class setter2, ...>
struct policy_selector : public setter1, public setter2, ... {
} ;

但是默认情况下传入的 setter 比如有同名的,比如都是 default_policies 就会出现问题。为此我们绕个弯子,

template <class base, int id>
struct discriminator : public base {} ;

template <class setter1, class setter2, ...>
struct policy_selector
  : public discriminator<setter1, 1>, public discriminator<setter2, 2>, ... {
} ;

最终用户使用的时候用法类似下面的:

duck<> my_default_duck ;
duck<set_quack<sound_policy>, set_fly<no_fly>, ...> my_super_duck ;

——————
And he believed in the LORD; and he counted it to him for righteousness.

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