C++ 杂谈(4)

function type

std::is_function 这个 type traits 是用来判断一个类型是不是 function type 的,需要注意的是尽管这玩意看起来跟 functor 有点类似,实际使用的时候有种种的微妙差异。

function type 可以 typedef,也可以用于 template parameter,甚至可以用来“声明”一个这个类型的对象,但是却不能用来定义或者其声明的对象无法进行赋值,即便这样我们还是可以传递这个类型的变量,比如下面的例子

template <typename Func>
struct foo {
  inline void operator()(Func f) {
    static_assert(std::is_function<Func>::value,
                  "given type is not a function type");
    std::cout << "hello function type: " << &f << '\n';
  }
};
template <typename Func>
inline void make_foo(Func f) {
  foo<typename std::remove_pointer<Func>::type>()(f);
}

void hello() {
}

foo<void()>()(hello);
make_foo(hello);

这里需要注意成员函数对应的 function type 并不是将成员函数指针 remove_pointer 之后的类型,或者说成员函数指针并不是指针(std::is_pointer 并不包括 std::is_member_(function_/object_)pointer),也就是说 member function 并不能当做某种特别的 function type 来使。这意味着如果传递一个 bar::bas 到 make_foo 是不会 work 的。

比较有意思的是 std::function 却提供了一个诡异的转换允许我们将一般的成员函数转换到 function type,但是这并不意味着成员函数是 function type。

std::function<void(bar*)> func = &bar::baz;
bar b;
func(&b);

更有意思的是 std::is_function<std::function<Func> >::value == false,也就是说 std::function 自己也不是 function type。说了这么多相信会对什么是 function type 有个更完整的认识。

和 functor 的区别与联系

有了以上认识以后我们跟 functor 来做一些比较,某些模板类、函数使用 functor,通常传递一个 functor 对象,常见的如 algorithm 里面诸如 foreach 等等。一个重要的区别在于:

  • function type 可以使用 boost.function_traits 里面的一些求 arity、return_type
  • functor 只能使用 std::result_of 获得返回类型,注意 result_of 的参数类型是 function type,比如 foo 是一个 functor,我们可以通过 std::result_of<foo()> 获得 foo::operator()() 的返回类型

std::function 提供了一个比较完整的 functor 的“容器”,通过 std::bind 产生的 functor,以及直接把 free function、成员函数 都能转换到 std::function 某个实例,这意味着 std::function 提供了一种绝佳的 delegation 的手段。回到正题上,在设计某些模板类的时候,什么时候应该用 function type 什么时候应该用 functor?很明显如果需要的 API 形式已知(比如 foreach 要一个 void(T) 这样的函数接口)functor 是常见选择。但是如果需要对不同的类型 API 进行特化处理的话,那就只能使用 function type 了。

unique_ptr 与 move

unique_ptr 通过禁用 copy constructor 保证它对资源的唯一所有权,有时候这样会导致一些困惑

struct foo {};

void bar(std::unique_ptr<foo> p) {
  // do something
}

std::unique_ptr<foo> f = new foo();
bar(f); // compile error
bar(std::move(f)); // this is okay

为什么前面一个调用编译通不过?很明显 f 是 lvalue,传递给 bar 的时候导致了 copy,std::move 将其转换到 rvalue ref,这时导致了 move constructor 被调用。

constexpr 的问题

一旦将一个函数定义成为 constexpr,就可能被人用到比如 template 参数里面,这意味着你很可能不能将这个行为改变成为非 compile-time 已知的 case。

initializer_list 的使用

pass by value,因为实现只用了指针之类的,可以用到 for each loop 里面。

lambda 的 early return 用法

如果有一个 nested loop,你希望中间某个条件满足后跳出来,但是 break 只能跳出一层,一个简单的策略是使用 lambda 将这部分 code 写成一个函数,这时可以使用 return 从中间退出来。

lambda 用于初始化

某些复杂的逻辑写在 constructor 里面并不合适,但是可以用 lambda 写成一个单独的 block,比较直观。

——————
Then the handmaidens came near, they and their children, and they bowed themselves

Advertisements
C++ 杂谈(4)

发表评论

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