C++ 模板(一)函数模板最基本的思想
一直以来,对 C++ 的模板和模板元编程,很多人不是视为洪水猛兽,就是当作无用鸡肋。即使是不那么偏激的,也说不定会以为这是只有库开发者才需要去掌握的「独门神功」。然而,当我真正去学习的时候,我发现,或许早期的 C++ 模板还有很多漏洞,但在 C++17 以后,模板已经成为了一套相对自然易学的工具。为了梳理学习脉络,我打算写一个 C++ 模板系列,分享自己的体会。
这个系列不是教材,所以我不会面面俱到地写,尤其是普通的模板语法。具体还是得看 C++ Templates (2nd) 这本书。
模板最基本的思想
世上存在三种多态:Subtyping, Ad Hoc Polymorphism, and Parametric Polymorphism。OOP 常用的继承等就是 Subtyping,函数重载和运算符重载指的是第二个,而模板则是最后一个(
听不懂(逃))。
或许,模板在最早期只是想做一个 "Better Macro",但它早已今非昔比,变成了一门完备的语言。如果你之前有过动态弱类型语言(如 Python, JavaScript)的经验,那学习模板时你可能会感到亲切——因为 C++ 中的模板就是一种 Duck Typing 的语言,这种「与普通 C++ 本身格格不入的模式」,使得 C++ 有了能媲美 JavaScript 的灵活性。
首先,我们通过一个简单的例子来说明什么是 Duck Typing。
Duck Typing
"If it looks like a duck and quacks like a duck, it must be a duck."
「如果一个东西长得像一只鸭子,叫得也像一只鸭子,那它就是一只鸭子。」
所谓「鸭子类型」,即不对一个对象的具体类型做出限制(是不是一只真正的鸭子),而是看它如何使用(能不能当作一只鸭子)。请看下面的代码:
template <typename Ptr>
void draw_element(Ptr UI_element) {
  UI_element->draw();
}
其中,
template <typename Ptr>是 C++ 中声明模板的方式。template关键字指出后面要声明一个模板,<>之间的内容则是「模板参数」。和普通的参数(在()里的参数)一样,模板参数会在模板被使用的时候替代为调用是提供的参数。// assume ChatWindow and ScrollBar all have a method named `draw()` void caller(ChatWindow* chat_window, ScrollBar* scroll_bar) { // T will be replaced by 'ChatWindow*' draw_element<ChatWindow*>(chat_window); // Template parameters can be omitted if can be auto-deducted // equivalent to 'draw_element<ScrollBar*>(scroll_bar)' draw_element(scroll_bar) }可以看到,模板参数需要在
<>中提供,普通参数在()中提供。如果可以推断出模板参数的类型,就可以不写模板参数。
传入的类型,既可以是一个 ChatWindow*,也可以是一个 ScrollBar*,模板函数 draw_element 并不限制它是什么具体类型,只要它能用在这个语句里:
UI_element->draw();
即使 ChatWindow 和  ScrollBar 没有共同基类,即使 ChatWindow::draw() 返回 void 而 ScrollBar::draw() 返回 int,也没有任何问题。
也可以传入一个 std::shared_ptr<ChatWindow>,因为 std::shared_ptr 重载了 -> 运算符。
指明类型约束
有时,我们希望能指定类型约束。这需要使用 concepts
template <typename Ptr>
concept IndirectlyDrawable = requires (Ptr iter) {
  iter->draw(); // 1
};
template <typename T> requires IndirectlyDrawable<T>
void draw_element(T element) {
  element->draw();
}
上面的 concept IndirectlyDrawable 指出一个 Ptr 类型的  ptr 必须可以用在 (1) 处的语句中。
事实上这个例子和上面的例子在使用上没什么差别,因为 draw_element 的函数体已经隐式地声明了对 T 的要求。如果不能满足,这个模板函数就无法调用。
这篇文章就写到这里吧,以后再一点点写更多的笔记。
References
- David Vandevoorde, Nicolai M.Josuttis, Douglas Gregor, C++ Templates (Second Edition)
 

                
            
        
浙公网安备 33010602011771号