【c++进阶】在c++11之前的编译器的努力

关注我,学习c++不迷路:

个人主页:爱装代码的小瓶子

专栏如下:

  1. c++学习
  2. Linux学习

后续会更新更多有趣的小知识,关注我带你遨游知识世界

期待你的关注。


文章目录

    • [第一章: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的发布显得如此珍贵和重要。

相关推荐
蜗牛love天空5 小时前
vs的运行库区别,静态连接mt和动态链接md运行库
c++
超级大福宝5 小时前
C++ 中 unordered_map 的 at() 和 []
数据结构·c++
蜗牛love天空5 小时前
智能指针的值传递和引用传递
开发语言·c++
☆cwlulu5 小时前
C语言关键字详解
开发语言
2301_797312265 小时前
学习Java26天
java·开发语言
cike_y5 小时前
JSP原理详解
java·开发语言·jsp
仰泳的熊猫5 小时前
1037 Magic Coupon
数据结构·c++·算法·pat考试
爱装代码的小瓶子5 小时前
【cpp进阶】c++11的新特性(概述版)
开发语言·c++
_OP_CHEN6 小时前
【从零开始的Qt开发指南】(十一)Qt常用控件之多元素控件与容器类控件深度解析
开发语言·qt·前端开发·多元素控件·gui开发·qt常用控件·容器类控件