C++模板 - 19(Function类模板的实现)

看来可能还真是文章的篇幅问题,我拆分后提交很快就审核通过了。

好了,回到正题。我们继续实现Function类模板。

 Function() noexcept : holder_(nullptr) {}

  Function(Function const &rhs)
      : holder_(rhs.holder_ == nullptr ? nullptr : rhs.holder_->clone()) {}

  Function(Function &rhs) noexcept
      : Function(static_cast(rhs)) {}

  Function(Function &&rhs) noexcept : holder_(rhs.holder_) {
    rhs.holder_ = nullptr;
  }

  template  Function(Callable &&c) : holder_(nullptr) {
    using Functor = std::decay_t;
    using Holder = FunctionStorage;
    holder_ = new Holder(std::forward(c));
  }

  ~Function() { delete holder_; }

我们看看它的构造函数的定义,都非常容易理解,使用构造参数合理地完成holder_的初始化:1. 缺省构造函数直接将内部对象指针设为null,这时没有实际的可调用对象;2. 拷贝构造函数利用虚拟拷贝接口完成复制;3. 移动构造函数就更加直接了,只是简单的一个指针的赋值,不过别忘记了将源对象的内部指针设为null,否则那个对象析构了,这里的holder就成悬挂指针了;4. 最后的模板构造函数稍微复杂一些,注意要将Callable的推导类型decay,防止引用类型的推导结果。最后一点,由于对象内部存储的是指针,别忘了在析构函数里释放内存。

  Function &operator=(Function const &rhs) {
    Function tmp(rhs);
    swap(*this, tmp);
    return *this;
  }

  Function &operator=(Function &rhs) {
    this->operator=(static_cast(rhs));
    return *this;
  }

  Function &operator=(Function &&rhs) noexcept {
    delete holder_;
    holder_ = rhs.holder_;
    rhs.holder_ = nullptr;
    return *this;
  }

  template  Function &operator=(Callable &&c) {
    Function tmp(std::forward(c));
    swap(*this, tmp);
    return *this;
  }

Function的赋值操作符的定义是实现“类型擦除”的关键,因为赋值对象的内部可调用对象可能与被赋值对象的当前内部对象类型不一样,不过这个差异被FunctionBase指针屏蔽了,这样拥有共同接口的类型在这里就当成是"同一类型"了,至少它们之间可以相互赋值。

  explicit operator bool() const { return holder_ != nullptr; }
  R operator()(Args... args) const { return holder_->call(args...); }

最后是Function的主要接口operator(),同时提供一个bool()转换运算符是方便用户检测它是否正确初始化了(是否可被调用)。好了,简化版本的std::function实现完毕。下面我们写一些测试代码来检验一下代码是否正确。

  Function f; // default ctor
  assert(!f);

我们先测试一下Function的缺省构造函数,顺便测试一下咱们定义的bool转换符。

double foo(int i, std::string s) {
  std::cout << "double foo(int, string)
";
  return 0.99;
}
Function f2 = foo; // ctor(T&&)
f2(3, "hello");

接着我们看一下用一个函数指针来构造Function对象,这个会调用最后定义的那个模板构造函数,然后测试一下Function对象的可调用性。

class X {
public:
	double operator()(int i, string s) const
	{
		cout << "double X::operator(int, string)
";
		return 32.3;
	}
};
f = X(); // operator=(T&&)
assert(f);

这里我们把一个X对象赋值给f,测试Function的移动语义赋值操作符,这个时候f对象就具备可调用性了。

其它的测试代码就不一一列出了,有兴趣的朋友请下载完整代码:

代码在functional目录

展开阅读全文

页面更新:2024-04-29

标签:模板   赋值   指针   函数   接口   定义   对象   类型   代码   测试

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top