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 的主要功能包括:
- 模板展开: 展示模板实例化的结果.
- 语法糖的展开: 将简化的语法转换为更基础的实现形式, 例如范围循环和 Lambda 表达式.
- 隐式行为显式化: 揭示默认构造函数, 隐式类型转换等编译器自动生成的代码.
- 协程展开: 展示协程在编译器中的分解过程.
- 内存对齐与填充: 通过展示结构体的填充字节, 优化内存布局.
- 生命周期可视化: 显示临时对象的创建和销毁时机.
- 类与继承的细节: 展示类的虚表布局. 展开继承关系和多态的实现细节.
这些功能为开发者提供了一个 “窥探编译器” 的窗口, 让许多编译器隐式的行为变得透明. 下面将为大家一一介绍.
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;
}
#endif
int main()
{
add(1, 2);
return 0;
}
2. 隐式类型转换
C++ Insights 展示隐式类型转换的行为. 例如:
int main() {
int a = 3.14; // 隐式转换 double -> int
return 0;
}
C++ Insight 输出:
int main()
{
int a = static_cast<int>(3.1400000000000001);
return 0;
}
3. 类型推导
展示 auto
的具体类型, 相对而言比较简单直白.
int main() {
auto i = 42; // 推导为int
auto s = "Hello"; // 推导为 const char*
auto d = 3.14; // 推导为double
return 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 输出:
注意此时函数func
和bar
没有默认参数
#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");
}
}
#endif
int 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: 1
char __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 无疑是一个值得尝试的工具!