当前位置: 首页 > news >正文

探索 C++ Insights: 理解编译器背后的 C++ 实现

C++ Insights 是什么?

C++ Insights 是一款强大的工具, 专注于揭示 C++ 代码在编译器内部的实现细节. 它能够帮助开发者深入理解模板展开, 隐式类型转换, 协程等特性背后的底层机制, 是学习和教学现代 C++ 的绝佳利器.

C++ Insights 如何工作

  • Clang Based Tool: C++ Insights 基于 Clang 编译器的前端实现, 利用 Clang 提供的抽象语法树(AST)进行转换. 它通过 Clang 的强大能力, 解析和处理 C++ 代码.
  • 使用最新版的 Clang: C++ Insights 始终与 Clang 的最新稳定版本保持同步, 这意味着它能够解析和支持 C++ 的最新标准特性(如 C++20 和 C++23).
  • 不带优化的版本: C++ Insights 不进行任何优化, 仅展示编译器前端的行为. 这意味着它生成的代码完全忠实于输入代码的原始逻辑, 而不会引入优化阶段的复杂性.

C++ Insights 可以做什么

C++ Insights 的主要功能包括:

  1. 模板展开: 展示模板实例化的结果.
  2. 语法糖的展开: 将简化的语法转换为更基础的实现形式, 例如范围循环和 Lambda 表达式.
  3. 隐式行为显式化: 揭示默认构造函数, 隐式类型转换等编译器自动生成的代码.
  4. 协程展开: 展示协程在编译器中的分解过程.
  5. 内存对齐与填充: 通过展示结构体的填充字节, 优化内存布局.
  6. 生命周期可视化: 显示临时对象的创建和销毁时机.
  7. 类与继承的细节: 展示类的虚表布局. 展开继承关系和多态的实现细节.

这些功能为开发者提供了一个 “窥探编译器” 的窗口, 让许多编译器隐式的行为变得透明. 下面将为大家一一介绍.

1. 模板展开

下面的代码展示了 C++如何处理模板代码:

template <typename T>
T add(T a, T b) {return a + b;
}int main() {add(1,2);
}

C++ Insight 输出:

template<typename T>
T add(T a, T b)
{return a + b;
}/* First instantiated from: insights.cpp:7 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
int add<int>(int a, int b)
{return a + b;
}
#endifint main()
{add(1, 2);return 0;
}

2. 隐式类型转换

C++ Insights 展示隐式类型转换的行为. 例如:

int main() {int a = 3.14;  // 隐式转换 double -> intreturn 0;
}

C++ Insight 输出:

int main()
{int a = static_cast<int>(3.1400000000000001);return 0;
}

3. 类型推导

展示 auto 的具体类型, 相对而言比较简单直白.

int main() {auto i = 42; // 推导为intauto s = "Hello"; // 推导为 const char*auto d = 3.14; // 推导为doublereturn 0;
}

C++ Insight 输出:

int main()
{int i = 42;const char * s = "Hello";double d = 3.1400000000000001;return 0;
}

4. 默认参数

函数的默认参数会在调用点被注入:

#include <string>void func(int x = 10) {}void bar(std::string s = "my default string") {}int main() {func();  // 使用默认参数bar();   // 使用默认参数bar();   // 使用默认参数return 0;
}

C++ Insight 输出:

注意此时函数funcbar没有默认参数

#include <string>void func(int x)
{
}void bar(std::basic_string<char, std::char_traits<char>, std::allocator<char> > s)
{
}int main()
{func(10);bar(std::basic_string<char, std::char_traits<char>, std::allocator<char> >("my default string", std::allocator<char>()));bar(std::basic_string<char, std::char_traits<char>, std::allocator<char> >("my default string", std::allocator<char>()));return 0;
}

5. 条件编译

constexpr if 分解为编译期可选分支:

#include <iostream>template <typename T>
void choose(T a) {if constexpr (std::is_integral_v<T>) {std::cout << "integer \n";} else {std::cout << "other type\n";}
}int main() {choose(0);choose("hello");return 0;
}

C++ Insight 输出:

#include <iostream>template<typename T>
void choose(T a)
{if constexpr(std::is_integral_v<T>) {std::operator<<(std::cout, "integer \n");} else /* constexpr */ {std::operator<<(std::cout, "other type\n");}}/* First instantiated from: insights.cpp:13 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void choose<int>(int a)
{if constexpr(true) {std::operator<<(std::cout, "integer \n");} else /* constexpr */ {}}
#endif/* First instantiated from: insights.cpp:14 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void choose<const char *>(const char * a)
{if constexpr(false) {} else /* constexpr */ {std::operator<<(std::cout, "other type\n");}}
#endifint main()
{choose(0);choose("hello");return 0;
}

6. 结构体 padding

struct Data {int i = 0;char c = 0;float f = 0;
};

C++ Insight 输出:

struct Data  /* size: 12, align: 4 */
{int i = 0;                      /* offset: 0, size: 4 */char c = 0;                     /* offset: 4, size: 1char __padding[3];                            size: 3 */float f = 0;                    /* offset: 8, size: 4 */
};

7. 范围循环

#include <vector>int main() {std::vector<int> nums{1, 2, 3};for (auto& n : nums) {}
}

C++ Insight 输出:

#include <vector>int main()
{std::vector<int, std::allocator<int> > nums = std::vector<int, std::allocator<int> >{std::initializer_list<int>{1, 2, 3}};{std::vector<int, std::allocator<int> > & __range1 = nums;std::__wrap_iter<int *> __begin1 = __range1.begin();std::__wrap_iter<int *> __end1 = __range1.end();for(; std::operator!=(__begin1, __end1); __begin1.operator++()) {int & n = __begin1.operator*();}}return 0;
}

8. Lambda 表达式

auto lambda = [](int x) { return x * 2; };

C++ Insight 输出:

class __lambda_1_15
{public:inline /*constexpr */ int operator()(int x) const{return x * 2;}using retType_1_15 = int (*)(int);inline constexpr operator retType_1_15 () const noexcept{return __invoke;};private:static inline /*constexpr */ int __invoke(int x){return __lambda_1_15{}.operator()(x);}public:// /*constexpr */ __lambda_1_15() = default;};__lambda_1_15 lambda = __lambda_1_15{};

9. 主函数默认返回值

int main() {}

C++ Insight 输出:

int main()
{return 0;
}

10. 对象生命周期

可视化临时对象的创建和销毁:

#include <iostream>
#include <vector>struct Bucket {std::vector<std::vector<int>> v;
};Bucket createBucket() {return Bucket{{{1, 2, 3},{4, 5, 6},}};
}int main() {for (auto vec : createBucket().v) {for (auto e : vec) {std::cout << e << ", ";}std::cout << "\n";}for (auto e : createBucket().v[0]) {std::cout << e << ',';}return 0;
}

C++ Insight 输出:

/************************************************************************************** NOTE: This an educational hand-rolled transformation. Things can be incorrect or  ** buggy.                                                                            **************************************************************************************/
#include <iostream>
#include <vector>struct Bucket
{std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > > v;// inline constexpr ~Bucket() noexcept = default;
};Bucket createBucket()
{const int __temporary10_15[3] = {1, 2, 3};const int __temporary11_15[3] = {4, 5, 6};const std::vector<int, std::allocator<int> > __temporary12_3[2] = {std::vector<int, std::allocator<int> >{std::initializer_list<int>{__temporary10_15, 3}}, std::vector<int, std::allocator<int> >{std::initializer_list<int>{__temporary11_15, 3}}};return Bucket{std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >{std::initializer_list<std::vector<int, std::allocator<int> > >{__temporary12_3, 2}}};__temporary12_3[0].~vector();__temporary12_3[1].~vector();/* __temporary11_15 // lifetime ends here *//* __temporary10_15 // lifetime ends here */;
}int main()
{{Bucket __temporary16_32 = createBucket();std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > > && __range1 = static_cast<std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > > &&>(__temporary16_32.v);std::__wrap_iter<std::vector<int, std::allocator<int> > *> __begin1 = __range1.begin();std::__wrap_iter<std::vector<int, std::allocator<int> > *> __end1 = __range1.end();for(; std::operator!=(__begin1, __end1); __begin1.operator++()) {std::vector<int, std::allocator<int> > vec = std::vector<int, std::allocator<int> >(__begin1.operator*());{std::vector<int, std::allocator<int> > & __range2 = vec;std::__wrap_iter<int *> __begin2 = __range2.begin();std::__wrap_iter<int *> __end2 = __range2.end();for(; std::operator!=(__begin2, __end2); __begin2.operator++()) {int e = __begin2.operator*();std::operator<<(std::cout.operator<<(e), ", ");/* e // lifetime ends here */}/* __end2 // lifetime ends here *//* __begin2 // lifetime ends here *//* __range2 // lifetime ends here */}std::operator<<(std::cout, "\n");vec.~vector();}/* __end1 // lifetime ends here *//* __begin1 // lifetime ends here */__temporary16_32.~Bucket();}{Bucket __temporary22_30 = createBucket();std::vector<int, std::allocator<int> > & __range1 = __temporary22_30.v.operator[](0);__temporary22_30.~Bucket();std::__wrap_iter<int *> __begin1 = __range1.begin();std::__wrap_iter<int *> __end1 = __range1.end();for(; std::operator!=(__begin1, __end1); __begin1.operator++()) {int e = __begin1.operator*();std::operator<<(std::cout.operator<<(e), ',');/* e // lifetime ends here */}/* __end1 // lifetime ends here *//* __begin1 // lifetime ends here *//* __range1 // lifetime ends here */}return 0;
}

11. 虚函数表

#include <iostream>class Base {public:virtual void call() { std::cout << "base called\n"; }virtual ~Base() = default;
};class Derived : public Base {public:void call() override { std::cout << "derived called\n"; }
};int main() {Base* p = nullptr;Derived d;p = &d;p->call();
}

C++ Insight 输出:

/************************************************************************************** NOTE: This an educational hand-rolled transformation. Things can be incorrect or  ** buggy.                                                                            **************************************************************************************/
#include <stddef.h> // NULL and more
void __cxa_start(void);
void __cxa_atexit(void);
typedef int (*__vptp)();struct __mptr
{short  d;short  i;__vptp f;
};extern struct __mptr* __vtbl_array[];#include <iostream>typedef struct Base
{__mptr * __vptrBase;
} Base;inline void callBase(Base * __this)
{std::operatorLessLess(&std::cout, "base called\n");
}inline void Destructor_Base(Base * __this)
{
}inline Base * operatorEqual(Base * __this, const Base * __rhs)
{return __this;
}inline Base * Constructor_Base(Base * __this)
{__this->__vptrBase = __vtbl_array[0];return __this;
}typedef struct Derived
{__mptr * __vptrBase;
} Derived;inline void callDerived(Derived * __this)
{std::operatorLessLess(&std::cout, "derived called\n");
}inline Derived * operatorEqual(Derived * __this, const Derived * __rhs)
{operatorEqual((Base *)__this, (Base *)__rhs);return __this;
}inline Derived * operatorEqual(Derived * __this, Derived * __rhs)
{operatorEqual((Base *)__this, (Base *)__rhs);return __this;
}inline void Destructor_Derived(Derived * __this)
{Destructor_Base((Base *)__this);
}inline Derived * Constructor_Derived(Derived * __this)
{Constructor_Base((Base *)__this);__this->__vptrBase = __vtbl_array[1];return __this;
}int __main(void)
{Base * p = NULL;Derived d;Constructor_Derived((Derived *)&d);p = (Base *)&d;(*((void (*)(Base *))((p)->__vptrBase[0]).f))((((Base *)(char *)(p)) + ((p)->__vptrBase[0]).d));return 0;/* d // lifetime ends here */
}int main(void)
{__cxa_start();int ret = __main();__cxa_atexit();return ret;/* ret // lifetime ends here */
}__mptr __vtbl_Base[2] = {{0, 0, (__vptp)callBase}, {0, 0, (__vptp)Destructor_Base}};
__mptr __vtbl_Derived[2] = {{0, 0, (__vptp)callDerived}, {0, 0, (__vptp)Destructor_Derived}};__mptr * __vtbl_array[2] = {__vtbl_Base, __vtbl_Derived};void __cxa_start(void)
{
}void __cxa_atexit(void)
{
}

小结

C++ Insights 是一个揭示 C++ 编译器行为的工具, 帮助开发者理解现代 C++ 特性和底层机制. 如果你希望深入理解 C++, C++ Insights 无疑是一个值得尝试的工具!

资源链接

  • C++ Insights
  • C++ Insights: Peek behind the curtains of your C++ compiler - Andreas Fertig

http://www.mrgr.cn/news/83407.html

相关文章:

  • 2024AAAI SCTNet论文阅读笔记
  • Uniapp中实现加载更多、下拉刷新、返回顶部功能
  • C语言二级考试
  • 解决el-table表格数据量过大导致页面卡顿问题 又名《umy-ui---虚拟表格仅渲染可视区域dom的神》
  • 如何选择适合的证件照制作软件,让您的照片制作更轻松
  • 记录一个移动端表格布局,就是一行标题,下面一列是对应的数据,一条一条的数据,还有点击数据进入详情的图标,还可以给一列加input输入框,还可以一对多
  • 树的模拟实现
  • python 个人学习笔记
  • RabbitMQ基础(简单易懂)
  • day06_Spark SQL
  • 【源码解析】Java NIO 包中的 ByteBuffer
  • 【Rust自学】11.7. 按测试的名称运行测试
  • Python|基于DeepSeek大模型,实现文本内容仿写(8)
  • MySql按年月日自动创建分区存储过程
  • 使用Struts2遇到的Context[项目名称]启动失败问题解决(Java Web学习笔记)
  • 《CPython Internals》阅读笔记:p96-p96
  • 20、Citrix 云桌面常见VDA注册问题汇总
  • HTTP 核心概念
  • 10.STM32F407ZGT6-内部温度传感器
  • 【论文+源码】一个基于Vue.js的MOBA类游戏攻略分享平台
  • Java SpringBoot + Vue + Uniapp 集成JustAuth 最快实现多端三方登录!(QQ登录、微信登录、支付宝登录……)
  • C++中 为什么要把基类指针指向子类对象?
  • Java 应用程序CPU 100%问题排查优化实战
  • 图像模糊度(清晰度)检测 EsFFT 算法详细分析
  • Java Web开发进阶——Spring Boot与Thymeleaf模板引擎
  • 计算机的错误计算(二百零八)