c++代码模板之 CRTP

简介: c++代码模板之 CRTP

本文将介绍一下c++代码模板的小技巧 -----  CRTP



虚函数


在介绍 CRTP 之前,我们先来了解下虚函数。


虚函数是通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数,从而实现了多态的特性。


一段简单的代码示例



class A
{
public:
 virtual void print()
 {
  std::cout << "Hello from A" << std::endl;
 }
};
class B : public A
{
public:
 void print() override
 {
  std::cout << "Hello from B" << std::endl;
 }
};



虚函数实现了多态的特性,但是每次调用的时候都要对虚函数表进行 look-up, 所以开销不低,较之直接调用具体对象的方法,虚函数调用通常会慢一个数量级以上。


在一些对性能敏感领域的软件系统中,比如OLAP数据库系统,需要对海量数据进行计算分析,虚函数的调用将会放大特别严重。



CRTP


奇异递归模板模式(Curiously Recurring Template Pattern,CRTP),CRTP是C++模板编程时的一种常见技巧(idiom):把派生类作为基类的模板参数。更一般地被称作F-bound polymorphism,是一类F 界量化。


  • CRTP 的基本范式


template <typename T>
class Base
{
    ...
};
class Derived : public Base<Derived>
{
    ...
};



这样做的目的在于在基类中使用派生类的方法,从基类的角度来看,派生类也是一个基类,基类可以通过static_cast将其转为派生类,从而静态使用派生类的成员和方法,如下:



template <typename T>
class Base
{
public:
    void doWhat()
    {
        T& derived = static_cast<T&>(*this);
        // use derived...
    }
};



  • 静态动态


Andrei Alexandrescu在Modern C++ Design中称 CRTP 为静态多态(static polymorphism)。


相比于普通继承方式实现的多台,CRTP可以在编译器实现类型的绑定,这种方式实现了虚函数的效果,同时也避免了动态多态的代价。



  • 权限控制


为了让基类能访问派生类的私有成员或方法,我们可以在派生类中和基类成为友元类。



friend class Base<Derived>;



  • std::enable_shared_from_this


假如在c++中想要在一个已被shareptr管理的类型对象内获取并返回this,为了防止被管理的对象已被智能指针释放,而导致this成为悬空指针,可能会考虑以share_ptr的形式返回this指针,我们可以使用 std::enable_shared_from_this, 它本身就是一种CRTP在标准库中的实现



struct FOO: std::enable_shared_from_this<FOO>
{
    std::shared_ptr<FOO> getptr() {
        return shared_from_this();
    }
};




  • CRPT 示例 (来自clickhouse源码)


/// Implement method to obtain an address of 'add' function.
template <typename Derived>
class IAggregateFunctionHelper : public IAggregateFunction
{
private:
    static void addFree(const IAggregateFunction * that, AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena * arena)
    {
        static_cast<const Derived &>(*that).add(place, columns, row_num, arena);
    }
public:
    IAggregateFunctionHelper(const DataTypes & argument_types_, const Array & parameters_)
        : IAggregateFunction(argument_types_, parameters_) {}
    AddFunc getAddressOfAddFunction() const override { return &addFree; }
    void addBatch(size_t batch_size, AggregateDataPtr * places, size_t place_offset, const IColumn ** columns, Arena * arena) const override
    {
        for (size_t i = 0; i < batch_size; ++i)
            static_cast<const Derived *>(this)->add(places[i] + place_offset, columns, i, arena);
    }
    void addBatchSinglePlace(size_t batch_size, AggregateDataPtr place, const IColumn ** columns, Arena * arena) const override
    {
        for (size_t i = 0; i < batch_size; ++i)
            static_cast<const Derived *>(this)->add(place, columns, i, arena);
    }
    void addBatchArray(
        size_t batch_size, AggregateDataPtr * places, size_t place_offset, const IColumn ** columns, const UInt64 * offsets, Arena * arena)
        const override
    {
        size_t current_offset = 0;
        for (size_t i = 0; i < batch_size; ++i)
        {
            size_t next_offset = offsets[i];
            for (size_t j = current_offset; j < next_offset; ++j)
                static_cast<const Derived *>(this)->add(places[i] + place_offset, columns, j, arena);
            current_offset = next_offset;
        }
    }
};



总结


如果想在编译期确定通过基类来得到派生类的行为,CRTP便是一种绝佳的选择, :)

相关文章
|
2月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
108 10
|
26天前
|
算法 安全 C++
提高C/C++代码的可读性
提高C/C++代码的可读性
41 4
|
2月前
|
编译器 程序员 C++
【C++打怪之路Lv7】-- 模板初阶
【C++打怪之路Lv7】-- 模板初阶
18 1
|
2月前
|
Linux C语言 C++
vsCode远程执行c和c++代码并操控linux服务器完整教程
这篇文章提供了一个完整的教程,介绍如何在Visual Studio Code中配置和使用插件来远程执行C和C++代码,并操控Linux服务器,包括安装VSCode、安装插件、配置插件、配置编译工具、升级glibc和编写代码进行调试的步骤。
314 0
vsCode远程执行c和c++代码并操控linux服务器完整教程
|
2月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
47 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
2月前
|
算法 编译器 C++
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
84 2
|
2月前
|
存储 编译器 C++
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
42 2
|
2月前
|
编译器 C++
【C++】模板进阶:深入解析模板特化
【C++】模板进阶:深入解析模板特化
|
14天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
25 2
|
20天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
54 5