关注我,学习c++不迷路:
个人主页:爱装代码的小瓶子
专栏如下:
- c++学习
- Linux学习
后续会更新更多有趣的小知识,关注我带你遨游知识世界
期待你的关注。
文章目录
- 第一章:C++11前夜 - 编译器的"军阀混战"时代
- 1.1 编译器的历史背景(1998-2011)
- 1.1.1 标准的滞后与编译器的"自由发挥"
- 1.1.2 三大编译器的"三国演义"
- 1.2 各编译器的"特色"与"缺陷"
- 1.2.1 GCC的"激进"与"保守"
- 1.2.2 Visual C++的"微软风格"
- 1.2.3 Clang的"后起之秀"
- 1.3 编译器的"补丁"与"扩展"
- 1.3.1 非标准扩展的泛滥
- 1.3.2 标准库的"方言"
- 1.4 开发者的噩梦:跨编译器编程
- 1.4.1 宏定义的"战争"
- 1.4.2 代码的"双版本"
- 1.4.3 模板的"噩梦"
- 1.5 编译器的"补丁"努力
- 1.5.1 GCC的"渐进式"支持策略
- 1.5.2 MSVC的"跳跃式"发展
- 1.5.3 Clang的"快速迭代"策略
- 1.6 新手选择编译器的"血泪史"
- 1.6.1 2009-2010年的选择困境
- 1.6.2 新手的"正确"选择(2010年)
- 1.7 编译器的"妥协"与"统一"
- 1.7.1 标准委员会的觉醒
- 1.7.2 编译器厂商的"竞赛"
- 1.8 总结:C++11前的编译器时代
- 1.8.1 编译器的"努力"总结
- 1.8.2 对开发者的启示
- 1.8.3 今天的建议
第一章:C++11前夜 - 编译器的"军阀混战"时代
1.1 编译器的历史背景(1998-2011)
1.1.1 标准的滞后与编译器的"自由发挥"
1998年C++标准发布后,标准委员会陷入了长达8年的"沉睡"。在这期间,编译器厂商面临一个尴尬的问题:
“标准说可以这样做,但没说必须怎么做,也没说不能怎么做…”
于是,各编译器厂商开始"自由发挥",导致C++代码在不同编译器间几乎无法移植。
1.1.2 三大编译器的"三国演义"
GCC (GNU Compiler Collection)
- 诞生:1987年,由Richard Stallman创建
- 特点:开源、免费、Linux霸主
- 版本:GCC 2.95(1999)、GCC 3.x(2001-2005)、GCC 4.x(2005-2012)
- 口号:“我们支持所有标准,包括那些还没制定的”
Visual C++ (MSVC)
- 诞生:1993年,微软为Windows打造
- 特点:商业软件、Windows平台王者、IDE集成
- 版本:VC++ 6.0(1998)、VC++ 2003(2003)、VC++ 2005(2005)、VC++ 2008(2008)、VC++ 2010(2010)
- 口号:“Windows是最好的开发平台,我们是最好的编译器”
Clang/LLVM
- 诞生:2007年,Apple为macOS/iOS开发
- 特点:模块化、快速、错误信息友好
- 版本:Clang 1.0(2007)、Clang 2.9(2010)
- 口号:“让编译错误不再是噩梦”
1.2 各编译器的"特色"与"缺陷"
1.2.1 GCC的"激进"与"保守"
GCC 2.95(1999)- “稳定但陈旧”
// GCC 2.95支持的C++特性template<typenameT>// 支持,但有bugclassvector{/* ... */};// 不支持的// export template(直到GCC 4.0才实现,后来又被移除)// 独有的扩展intx=5;intarr[x];// GCC支持VLA(变长数组),但标准C++不支持GCC 3.x(2001-2005)- “标准支持的挣扎”
// GCC 3.4开始支持部分C++98标准// 但仍有大量bug// 问题1:模板两阶段查找template<typenameT>voidfoo(T t){bar(t);// GCC 3.x:在实例化时查找// 标准:应该在定义时查找}voidbar(int){}templatevoidfoo<int>(int);// 实例化GCC 4.0-4.7(2005-2012)- “C++11支持的序曲”
// GCC 4.3开始支持部分C++11特性(作为扩展)// 但需要-std=c++0x编译选项// GCC 4.4支持:// - auto(实验性)// - decltype(实验性)// - 可变参数模板(有bug)// GCC 4.5支持:// - Lambda表达式(不完整)// - static_assert// GCC 4.6支持:// - nullptr// - range-based for(不完整)// GCC 4.7支持:// - 完整的C++11支持(大部分)1.2.2 Visual C++的"微软风格"
VC++ 6.0(1998)- “C++98的噩梦”
// VC++ 6.0的"特色"// 1. 模板支持极差template<typenameT>classMyVector{// 不支持嵌套模板类型// MyVector<MyVector<int>> v; // 错误!};// 2. STL实现有bugstd::vector<int>v;v.push_back(1);std::vector<int>::iterator it=v.begin();*it=2;// 可能崩溃!// 3. 标准库不完整// 没有<stdexcept>,没有<stdint.h>等// 4. 独有的语言扩展__declspec(dllexport)voidfunc();// Windows专用VC++ 2005(2005)- “安全检查的代价”
// 引入了安全的CRT函数charbuf[10];strcpy_s(buf,10,"hello");// 安全版本,需要长度参数// 但对标准C++支持仍然有限// 不支持export template// 模板两阶段查找错误// 独有的预处理器#ifdef_MSC_VER// 微软特定代码#endifVC++ 2008(2008)- “C++0x的曙光”
// 开始实验性支持C++0x// 但需要/tc或/std:c++0x选项// 支持:// - auto(部分)// - static_assert// - rvalue引用(实验性)// 不支持:// - Lambda// - 可变参数模板// - decltypeVC++ 2010(2010)- “C++0x的半成品”
// 支持:// - Lambda表达式(但捕获有bug)// - auto// - decltype// - static_assert// - 可变参数模板(有限支持)// - 右值引用(但实现有缺陷)// 不支持:// - 初始化列表// - constexpr// - 委托构造函数// - 继承构造函数// - 用户定义字面量// 独有的bug:autolambda=[](intx){returnx*2;};// 在某些情况下,lambda的类型推导错误1.2.3 Clang的"后起之秀"
Clang 1.0-2.9(2007-2010)- “挑战者”
// Clang的设计目标:// 1. 快速编译(比GCC快3-5倍)// 2. 友好的错误信息// 3. 模块化架构// 早期支持的C++11特性(2009-2010):// - Lambda(较早实现)// - auto// - decltype// - 可变参数模板// 错误信息对比:// GCC 4.4:// error: no matching function for call to 'foo'// note: candidate: void foo(int)// note: candidate: void foo(double)// Clang 0.9:// error: no matching function for call to 'foo'// foo(5.5);// ^~~// note: candidate: void foo(int)// void foo(int x);// ^// note: candidate: void foo(double)// void foo(double x);// ^// (更清晰,指出调用位置)1.3 编译器的"补丁"与"扩展"
1.3.1 非标准扩展的泛滥
GCC的扩展:
// 1. 语句表达式intx=({inta=5;intb=10;a+b;});// x = 15// 2. 内建函数intx=__builtin_popcount(0xFF);// 计算1的个数// 3. 属性voidfunc()__attribute__((noreturn));// 4. VLA(变长数组)voidfunc(intn){intarr[n];// 标准C++不允许}MSVC的扩展:
// 1. __declspec__declspec(dllexport)voidexport_func();__declspec(dllimport)voidimport_func();__declspec(align(16))structAlignedStruct{};// 2. 内存对齐#pragmapack(push,1)structPackedStruct{chara;intb;};#pragmapack(pop)// 3. 循环优化#pragmaloop(hint_parallel(4))for(inti=0;i<n;++i){// ...}// 4. 异常规范(非标准)voidfunc()throw(int,double);// VC++支持,但标准已废弃Clang的扩展:
// 1. 属性语法[[clang::always_inline]]voidfunc(){}// 2. 块表达式(来自Objective-C)int(^block)(int)=^(intx){returnx*2;};// 3. 类型特征扩展static_assert(__has_feature(cxx_rvalue_references),"Need rvalue refs");1.3.2 标准库的"方言"
GCC的libstdc++:
// GCC 4.4的std::vectornamespacestd{template<typenameT>classvector{// 实现细节T*_M_impl;// GCC特有的命名约定// 扩展接口void_M_fill_insert(iterator position,size_type n,constT&x);};}// 使用扩展std::vector<int>v;v._M_impl;// 访问内部(危险!)MSVC的STL:
// VC++ 2010的std::vectornamespacestd{template<typenameT>classvector{// 微软特有的实现T*_Myfirst;T*_Mylast;T*_Myend;// 扩展void_Insert_n(iterator where,size_type count,constT&val);};}// 使用扩展std::vector<int>v;v._Myfirst;// 访问内部Clang的libc++:
// libc++的设计目标:标准符合性// 不提供扩展,严格遵循标准1.4 开发者的噩梦:跨编译器编程
1.4.1 宏定义的"战争"
// 为了兼容不同编译器,需要写大量宏// 检测编译器版本#ifdefined(_MSC_VER)// MSVC特定代码#if_MSC_VER<1600// VC++ 2010之前,不支持C++0x#endif#elifdefined(__GNUC__)// GCC特定代码#if__GNUC__<4||(__GNUC__==4&&__GNUC_MINOR__<4)// GCC 4.4之前,不支持C++0x#endif#endif// 检测C++11支持#if__cplusplus>=201103L// C++11支持#else// 不支持,使用替代方案#endif// 检测特定特性#ifdefined(__clang__)#if__has_feature(cxx_rvalue_references)// 支持右值引用#endif#endif1.4.2 代码的"双版本"
// 为了兼容,经常需要写两套代码// 版本1:C++03classMyClass{private:int*data;public:MyClass():data(newint[10]){}~MyClass(){delete[]data;}// 拷贝构造和赋值(深拷贝)MyClass(constMyClass&other){data=newint[10];std::copy(other.data,other.data+10,data);}MyClass&operator=(constMyClass&other){if(this!=&other){delete[]data;data=newint[10];std::copy(other.data,other.data+10,data);}return*this;}};// 版本2:C++11classMyClass{private:std::unique_ptr<int[]>data;public:MyClass():data(std::make_unique<int[]>(10)){}// 自动生成移动构造和移动赋值MyClass(MyClass&&)=default;MyClass&operator=(MyClass&&)=default;// 拷贝构造和赋值(删除或实现)MyClass(constMyClass&other){data=std::make_unique<int[]>(10);std::copy(other.data.get(),other.data.get()+10,data.get());}MyClass&operator=(constMyClass&other){if(this!=&other){data=std::make_unique<int[]>(10);std::copy(other.data.get(),other.data.get()+10,data.get());}return*this;}};// 为了兼容,需要写:#if__cplusplus>=201103L// 使用版本2#else// 使用版本1#endif1.4.3 模板的"噩梦"
// C++03中,可变参数模板的替代方案// 1. 使用多个重载voidprint(){}voidprint(inta){std::cout<<a;}voidprint(inta,intb){std::cout<<a<<b;}voidprint(inta,intb,intc){std::cout<<a<<b<<c;}// ... 需要写几十个版本// 2. 使用boost::preprocessor#include<boost/preprocessor.hpp>#definePRINT(z,n,data)\template<BOOST_PP_ENUM_PARAMS(n,typenameT)>\voidprint(BOOST_PP_ENUM_BINARY_PARAMS(n,T,arg)){\/* 实现 */\}BOOST_PP_REPEAT(10,PRINT,~)// 生成10个版本// 3. 使用递归继承template<typenameT1=void,typenameT2=void,typenameT3=void>structprint_impl{staticvoidprint(T1 a,T2 b,T3 c){std::cout<<a<<b<<c;}};template<>structprint_impl<void,void,void>{staticvoidprint(){}};template<typenameT1>structprint_impl<T1,void,void>{staticvoidprint(T1 a){std::cout<<a;}};// 使用print_impl<int,double,char>::print(1,2.5,'a');1.5 编译器的"补丁"努力
1.5.1 GCC的"渐进式"支持策略
GCC采用"实验性→稳定→标准"的策略:
// GCC 4.3:实验性支持// 编译选项:-std=c++0x// 警告:这是实验性功能,可能改变// GCC 4.4:部分稳定// 支持:auto, decltype, 可变参数模板// Bug:Lambda捕获有问题// GCC 4.5:改进// 支持:Lambda(改进版)// Bug:右值引用实现不完整// GCC 4.6:接近完成// 支持:nullptr, range-based for(部分)// Bug:constexpr不支持循环// GCC 4.7:完整支持// 支持:大部分C++11// 警告:某些边缘情况仍有问题1.5.2 MSVC的"跳跃式"发展
MSVC采用"大版本跳跃"策略:
// VC++ 2008:几乎不支持C++0x// VC++ 2010:支持部分C++0x// VC++ 2012:支持更多C++11// VC++ 2013:支持大部分C++11// VC++ 2015:完整支持C++11// 但MSVC有"特色":// 1. 某些特性需要/tc或/std:c++14选项// 2. 某些特性有bug且长期不修复// 3. 某些特性实现与标准不一致1.5.3 Clang的"快速迭代"策略
Clang采用"快速发布,快速修复"策略:
// Clang 1.0:基本支持C++0x// Clang 2.9:大部分支持// Clang 3.0:完整支持C++11// 优势:// 1. 错误信息极其友好// 2. 对标准支持最积极// 3. 模块化设计,易于扩展// 劣势:// 1. 早期版本优化不如GCC// 2. 标准库libc++早期不成熟1.6 新手选择编译器的"血泪史"
1.6.1 2009-2010年的选择困境
场景1:Linux开发者
# 选择1:GCC 4.4(系统自带)# 优点:免费,稳定,Linux标准# 缺点:C++11支持不完整,Lambda有bug# 选择2:GCC 4.5(手动编译)# 优点:更好的C++11支持# 缺点:编译GCC本身需要几小时# 选择3:Clang 2.9(手动安装)# 优点:编译快,错误信息好# 缺点:优化不如GCC,标准库不成熟场景2:Windows开发者
# 选择1:VC++ 2010# 优点:IDE集成好,调试方便# 缺点:C++11支持有限,编译速度慢# 选择2:MinGW(GCC for Windows)# 优点:接近GCC的C++11支持# 缺点:IDE集成差,调试困难# 选择3:Intel C++ Compiler# 优点:优化最好,兼容GCC/MSVC# 缺点:商业软件,昂贵场景3:跨平台开发者
// 恶梦般的配置#ifdef_WIN32#ifdef_MSC_VER// MSVC特定代码#if_MSC_VER<1600// 不支持C++11,使用boost#include<boost/shared_ptr.hpp>#else// 支持部分C++11#include<memory>#endif#else// MinGW#include<memory>#endif#else// Linux/Mac#include<memory>#endif// 代码中#if__cplusplus>=201103Lusingstd::shared_ptr;#elseusingboost::shared_ptr;#endif1.6.2 新手的"正确"选择(2010年)
2010年的建议:
# 1. 学习C++11:使用GCC 4.5或Clang 2.9# 原因:支持大部分特性,免费# 2. 生产环境:使用GCC 4.4或VC++ 2010# 原因:稳定,有支持# 3. 跨平台:使用GCC + CMake# 原因:跨平台一致性# 4. 避免:VC++ 6.0(已过时)# GCC 3.x(太旧)# 任何不支持C++11的编译器1.7 编译器的"妥协"与"统一"
1.7.1 标准委员会的觉醒
2009年,标准委员会意识到:
- 社区已经通过Boost等库实现了C++11特性
- 编译器厂商已经在部分实现
- 再不发布标准,C++将分裂成多个方言
于是,C++0x改名C++11,目标2011年发布。
1.7.2 编译器厂商的"竞赛"
2010-2012年,三大编译器开始竞赛:
GCC:
- 2010年:GCC 4.5,Lambda初步支持
- 2011年:GCC 4.6,nullptr支持
- 2012年:GCC 4.7,完整C++11支持
MSVC:
- 2010年:VC++ 2010,部分C++0x
- 2012年:VC++ 2012,更多C++11
- 2013年:VC++ 2013,接近完整
- 2015年:VC++ 2015,完整C++11
Clang:
- 2010年:Clang 2.9,大部分支持
- 2011年:Clang 3.0,完整支持
- 2012年:Clang 3.1,C++11完整
1.8 总结:C++11前的编译器时代
1.8.1 编译器的"努力"总结
GCC的努力:
- ✅ 渐进式支持,从实验到稳定
- ✅ 保持向后兼容
- ✅ 提供大量扩展
- ❌ 早期版本bug较多
- ❌ 编译速度慢
MSVC的努力:
- ✅ IDE集成优秀
- ✅ 调试体验好
- ✅ Windows平台优化
- ❌ C++11支持滞后
- ❌ 非标准扩展多
- ❌ 编译速度极慢
Clang的努力:
- ✅ 快速编译
- ✅ 友好错误信息
- ✅ 标准符合性高
- ❌ 早期优化不足
- ❌ 标准库不成熟
1.8.2 对开发者的启示
C++11之前:
- 编写跨平台C++是噩梦
- 需要大量宏和条件编译
- 模板编程极其困难
- 内存管理需要手动小心
- 多线程需要平台特定代码
C++11之后:
- 编译器统一支持标准
- 代码可移植性大幅提升
- 开发效率提高10倍
- 内存安全得到保障
- 并发编程标准化
1.8.3 今天的建议
如果你现在学习C++:
# 编译器选择:# 1. GCC 13+(Linux/Mac/Windows)# 2. Clang 17+(Linux/Mac/Windows)# 3. MSVC 2022(Windows)# 编译选项:g++ -std=c++23 -O2 -Wall -Wextra -pedantic main.cpp# 或者使用CMake:cmake_minimum_required(VERSION3.20)project(myproject)set(CMAKE_CXX_STANDARD23)set(CMAKE_CXX_STANDARD_REQUIRED ON)其实如果你只是想尝试编译器,我更推荐:
C++11前的编译器时代告诉我们:
标准的缺失会导致混乱,而标准的统一能带来繁荣。C++11不仅是一个语言版本,更是编译器厂商和开发者社区的"停战协议"。
这就是C++11之前编译器的故事——一段充满"补丁"、“扩展”、"妥协"和"竞赛"的混乱岁月。正是这段经历,让C++11的发布显得如此珍贵和重要。