
关注我,学习c++不迷路:
专栏如下:
后续会更新更多有趣的小知识,关注我带你遨游知识世界

期待你的关注。

文章目录
-
- [第一章:C++11前夜 - 编译器的"军阀混战"时代](#第一章:C++11前夜 - 编译器的"军阀混战"时代)
-
- [1.1 编译器的历史背景(1998-2011)](#1.1 编译器的历史背景(1998-2011))
-
- [1.1.1 标准的滞后与编译器的"自由发挥"](#1.1.1 标准的滞后与编译器的"自由发挥")
- [1.1.2 三大编译器的"三国演义"](#1.1.2 三大编译器的"三国演义")
- [1.2 各编译器的"特色"与"缺陷"](#1.2 各编译器的"特色"与"缺陷")
-
- [1.2.1 GCC的"激进"与"保守"](#1.2.1 GCC的"激进"与"保守")
- [1.2.2 Visual C++的"微软风格"](#1.2.2 Visual C++的"微软风格")
- [1.2.3 Clang的"后起之秀"](#1.2.3 Clang的"后起之秀")
- [1.3 编译器的"补丁"与"扩展"](#1.3 编译器的"补丁"与"扩展")
-
- [1.3.1 非标准扩展的泛滥](#1.3.1 非标准扩展的泛滥)
- [1.3.2 标准库的"方言"](#1.3.2 标准库的"方言")
- [1.4 开发者的噩梦:跨编译器编程](#1.4 开发者的噩梦:跨编译器编程)
-
- [1.4.1 宏定义的"战争"](#1.4.1 宏定义的"战争")
- [1.4.2 代码的"双版本"](#1.4.2 代码的"双版本")
- [1.4.3 模板的"噩梦"](#1.4.3 模板的"噩梦")
- [1.5 编译器的"补丁"努力](#1.5 编译器的"补丁"努力)
-
- [1.5.1 GCC的"渐进式"支持策略](#1.5.1 GCC的"渐进式"支持策略)
- [1.5.2 MSVC的"跳跃式"发展](#1.5.2 MSVC的"跳跃式"发展)
- [1.5.3 Clang的"快速迭代"策略](#1.5.3 Clang的"快速迭代"策略)
- [1.6 新手选择编译器的"血泪史"](#1.6 新手选择编译器的"血泪史")
-
- [1.6.1 2009-2010年的选择困境](#1.6.1 2009-2010年的选择困境)
- [1.6.2 新手的"正确"选择(2010年)](#1.6.2 新手的"正确"选择(2010年))
- [1.7 编译器的"妥协"与"统一"](#1.7 编译器的"妥协"与"统一")
-
- [1.7.1 标准委员会的觉醒](#1.7.1 标准委员会的觉醒)
- [1.7.2 编译器厂商的"竞赛"](#1.7.2 编译器厂商的"竞赛")
- [1.8 总结:C++11前的编译器时代](#1.8 总结:C++11前的编译器时代)
-
- [1.8.1 编译器的"努力"总结](#1.8.1 编译器的"努力"总结)
- [1.8.2 对开发者的启示](#1.8.2 对开发者的启示)
- [1.8.3 今天的建议](#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)- "稳定但陈旧"
cpp
// GCC 2.95支持的C++特性
template<typename T> // 支持,但有bug
class vector { /* ... */ };
// 不支持的
// export template(直到GCC 4.0才实现,后来又被移除)
// 独有的扩展
int x = 5;
int arr[x]; // GCC支持VLA(变长数组),但标准C++不支持
GCC 3.x(2001-2005)- "标准支持的挣扎"
cpp
// GCC 3.4开始支持部分C++98标准
// 但仍有大量bug
// 问题1:模板两阶段查找
template<typename T>
void foo(T t) {
bar(t); // GCC 3.x:在实例化时查找
// 标准:应该在定义时查找
}
void bar(int) {}
template void foo<int>(int); // 实例化
GCC 4.0-4.7(2005-2012)- "C++11支持的序曲"
cpp
// 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的噩梦"
cpp
// VC++ 6.0的"特色"
// 1. 模板支持极差
template<typename T>
class MyVector {
// 不支持嵌套模板类型
// MyVector<MyVector<int>> v; // 错误!
};
// 2. STL实现有bug
std::vector<int> v;
v.push_back(1);
std::vector<int>::iterator it = v.begin();
*it = 2; // 可能崩溃!
// 3. 标准库不完整
// 没有<stdexcept>,没有<stdint.h>等
// 4. 独有的语言扩展
__declspec(dllexport) void func(); // Windows专用
VC++ 2005(2005)- "安全检查的代价"
cpp
// 引入了安全的CRT函数
char buf[10];
strcpy_s(buf, 10, "hello"); // 安全版本,需要长度参数
// 但对标准C++支持仍然有限
// 不支持export template
// 模板两阶段查找错误
// 独有的预处理器
#ifdef _MSC_VER
// 微软特定代码
#endif
VC++ 2008(2008)- "C++0x的曙光"
cpp
// 开始实验性支持C++0x
// 但需要/tc或/std:c++0x选项
// 支持:
// - auto(部分)
// - static_assert
// - rvalue引用(实验性)
// 不支持:
// - Lambda
// - 可变参数模板
// - decltype
VC++ 2010(2010)- "C++0x的半成品"
cpp
// 支持:
// - Lambda表达式(但捕获有bug)
// - auto
// - decltype
// - static_assert
// - 可变参数模板(有限支持)
// - 右值引用(但实现有缺陷)
// 不支持:
// - 初始化列表
// - constexpr
// - 委托构造函数
// - 继承构造函数
// - 用户定义字面量
// 独有的bug:
auto lambda = [](int x) { return x * 2; };
// 在某些情况下,lambda的类型推导错误
1.2.3 Clang的"后起之秀"
Clang 1.0-2.9(2007-2010)- "挑战者"
cpp
// 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的扩展:
cpp
// 1. 语句表达式
int x = ({
int a = 5;
int b = 10;
a + b;
}); // x = 15
// 2. 内建函数
int x = __builtin_popcount(0xFF); // 计算1的个数
// 3. 属性
void func() __attribute__((noreturn));
// 4. VLA(变长数组)
void func(int n) {
int arr[n]; // 标准C++不允许
}
MSVC的扩展:
cpp
// 1. __declspec
__declspec(dllexport) void export_func();
__declspec(dllimport) void import_func();
__declspec(align(16)) struct AlignedStruct {};
// 2. 内存对齐
#pragma pack(push, 1)
struct PackedStruct {
char a;
int b;
};
#pragma pack(pop)
// 3. 循环优化
#pragma loop(hint_parallel(4))
for (int i = 0; i < n; ++i) {
// ...
}
// 4. 异常规范(非标准)
void func() throw(int, double); // VC++支持,但标准已废弃
Clang的扩展:
cpp
// 1. 属性语法
[[clang::always_inline]] void func() {}
// 2. 块表达式(来自Objective-C)
int (^block)(int) = ^(int x) { return x * 2; };
// 3. 类型特征扩展
static_assert(__has_feature(cxx_rvalue_references), "Need rvalue refs");
1.3.2 标准库的"方言"
GCC的libstdc++:
cpp
// GCC 4.4的std::vector
namespace std {
template<typename T>
class vector {
// 实现细节
T* _M_impl; // GCC特有的命名约定
// 扩展接口
void _M_fill_insert(iterator position, size_type n, const T& x);
};
}
// 使用扩展
std::vector<int> v;
v._M_impl; // 访问内部(危险!)
MSVC的STL:
cpp
// VC++ 2010的std::vector
namespace std {
template<typename T>
class vector {
// 微软特有的实现
T* _Myfirst;
T* _Mylast;
T* _Myend;
// 扩展
void _Insert_n(iterator where, size_type count, const T& val);
};
}
// 使用扩展
std::vector<int> v;
v._Myfirst; // 访问内部
Clang的libc++:
cpp
// libc++的设计目标:标准符合性
// 不提供扩展,严格遵循标准
1.4 开发者的噩梦:跨编译器编程
1.4.1 宏定义的"战争"
cpp
// 为了兼容不同编译器,需要写大量宏
// 检测编译器版本
#if defined(_MSC_VER)
// MSVC特定代码
#if _MSC_VER < 1600
// VC++ 2010之前,不支持C++0x
#endif
#elif defined(__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
// 检测特定特性
#if defined(__clang__)
#if __has_feature(cxx_rvalue_references)
// 支持右值引用
#endif
#endif
1.4.2 代码的"双版本"
cpp
// 为了兼容,经常需要写两套代码
// 版本1:C++03
class MyClass {
private:
int* data;
public:
MyClass() : data(new int[10]) {}
~MyClass() { delete[] data; }
// 拷贝构造和赋值(深拷贝)
MyClass(const MyClass& other) {
data = new int[10];
std::copy(other.data, other.data + 10, data);
}
MyClass& operator=(const MyClass& other) {
if (this != &other) {
delete[] data;
data = new int[10];
std::copy(other.data, other.data + 10, data);
}
return *this;
}
};
// 版本2:C++11
class MyClass {
private:
std::unique_ptr<int[]> data;
public:
MyClass() : data(std::make_unique<int[]>(10)) {}
// 自动生成移动构造和移动赋值
MyClass(MyClass&&) = default;
MyClass& operator=(MyClass&&) = default;
// 拷贝构造和赋值(删除或实现)
MyClass(const MyClass& other) {
data = std::make_unique<int[]>(10);
std::copy(other.data.get(), other.data.get() + 10, data.get());
}
MyClass& operator=(const MyClass& 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
#endif
1.4.3 模板的"噩梦"
cpp
// C++03中,可变参数模板的替代方案
// 1. 使用多个重载
void print() {}
void print(int a) { std::cout << a; }
void print(int a, int b) { std::cout << a << b; }
void print(int a, int b, int c) { std::cout << a << b << c; }
// ... 需要写几十个版本
// 2. 使用boost::preprocessor
#include <boost/preprocessor.hpp>
#define PRINT(z, n, data) \
template<BOOST_PP_ENUM_PARAMS(n, typename T)> \
void print(BOOST_PP_ENUM_BINARY_PARAMS(n, T, arg)) { \
/* 实现 */ \
}
BOOST_PP_REPEAT(10, PRINT, ~) // 生成10个版本
// 3. 使用递归继承
template<typename T1 = void, typename T2 = void, typename T3 = void>
struct print_impl {
static void print(T1 a, T2 b, T3 c) {
std::cout << a << b << c;
}
};
template<>
struct print_impl<void, void, void> {
static void print() {}
};
template<typename T1>
struct print_impl<T1, void, void> {
static void print(T1 a) {
std::cout << a;
}
};
// 使用
print_impl<int, double, char>::print(1, 2.5, 'a');
1.5 编译器的"补丁"努力
1.5.1 GCC的"渐进式"支持策略
GCC采用"实验性→稳定→标准"的策略:
cpp
// 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采用"大版本跳跃"策略:
cpp
// 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采用"快速发布,快速修复"策略:
cpp
// 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开发者
bash
# 选择1:GCC 4.4(系统自带)
# 优点:免费,稳定,Linux标准
# 缺点:C++11支持不完整,Lambda有bug
# 选择2:GCC 4.5(手动编译)
# 优点:更好的C++11支持
# 缺点:编译GCC本身需要几小时
# 选择3:Clang 2.9(手动安装)
# 优点:编译快,错误信息好
# 缺点:优化不如GCC,标准库不成熟
场景2:Windows开发者
bash
# 选择1:VC++ 2010
# 优点:IDE集成好,调试方便
# 缺点:C++11支持有限,编译速度慢
# 选择2:MinGW(GCC for Windows)
# 优点:接近GCC的C++11支持
# 缺点:IDE集成差,调试困难
# 选择3:Intel C++ Compiler
# 优点:优化最好,兼容GCC/MSVC
# 缺点:商业软件,昂贵
场景3:跨平台开发者
cpp
// 恶梦般的配置
#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 >= 201103L
using std::shared_ptr;
#else
using boost::shared_ptr;
#endif
1.6.2 新手的"正确"选择(2010年)
2010年的建议:
bash
# 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++:
bash
# 编译器选择:
# 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(VERSION 3.20)
project(myproject)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
其实如果你只是想尝试编译器,我更推荐:

C++11前的编译器时代告诉我们:
标准的缺失会导致混乱,而标准的统一能带来繁荣。C++11不仅是一个语言版本,更是编译器厂商和开发者社区的"停战协议"。
这就是C++11之前编译器的故事------一段充满"补丁"、"扩展"、"妥协"和"竞赛"的混乱岁月。正是这段经历,让C++11的发布显得如此珍贵和重要。