C++20新特性_[[no_unique_address]]属性

文章目录

  • [第一章 C++20核心语法特性](#第一章 C++20核心语法特性)
    • [1.10 [[no_unique_address]]属性](#1.10 [[no_unique_address]]属性)
      • [1.10.1 编译器优化原理](#1.10.1 编译器优化原理)
      • [1.10.2 举例说明](#1.10.2 举例说明)

本文记录C++20新特性之[[no_unique_address]]属性。

第一章 C++20核心语法特性

1.10 [[no_unique_address]]属性

在 C++ 标准中,为了保证每个对象都有唯一的内存地址(以便指针区分),即使是一个空类(Empty Class,没有任何数据成员,只有函数),其实例化后的大小也至少是 1 字节。

cpp 复制代码
    struct Empty {}; // 大小为 1 字节

    struct Wrapper {
        int id;
        Empty e; // 占用 1 字节,加上内存对齐,Wrapper 8 字节
    };

    void test()
    {
        cout << sizeof(Empty) << endl;
        // 1 
		cout << sizeof(Wrapper) << endl;
        // 8
    }

在泛型编程中,我们经常需要将一些无状态的策略类(Policy Classes,如分配器 Allocator、比较器 Comparator)作为成员变量存储。如果这些类是空的,它们占用的这 1 字节(加上对齐填充)就是纯粹的内存浪费。

在 C++20 之前,为了消除这 1 字节的开销,不得不使用复杂的 EBO (空基类优化) 技巧,强行让 Wrapper 继承 Empty,而不是包含它。这导致代码结构变得扭曲且难以阅读。

EBO优化如下:

cpp 复制代码
    struct Empty {}; // 大小为 1 字节

    struct Wrapper {
        int id;
        Empty e; // 占用 1 字节,加上内存对齐,Wrapper 可能变成 8 字节
    };

    // 使用 EBO 的版本
    struct WrapperEBO : private Empty { // 通过继承来应用 EBO
        int id;
    };

    void test()
    {
        std::cout << "sizeof(Empty): " << sizeof(Empty) << std::endl;
        // 输出: 1
        std::cout << "sizeof(Wrapper) [未优化]: " << sizeof(Wrapper) << std::endl;
        // 输出: 8 (在典型的64位系统上,4字节int + 1字节Empty + 3字节对齐填充)
        std::cout << "sizeof(WrapperEBO) [EBO 优化]: " << sizeof(WrapperEBO) << std::endl;
        // 输出: 4 (Empty 基类的大小被优化掉了)
    }

C++20 引入了一个新的属性 [[no_unique_address]],它打破了"每个成员变量必须占用独立地址"的限制,为类成员的内存布局优化带来了革命性的变化。

1.10.1 编译器优化原理

\[no_unique_address\]\] 是一个属性(Attribute),用于修饰类的非静态数据成员。 作用:告诉编译器,被\[no_unique_address\] 修饰的变量不需要拥有唯一的内存地址。 效果: 如果该成员是空类:编译器可以将其大小优化为 0。它在内存中不占用任何空间,就像它不存在一样。 如果该成员非空:编译器可以将其与其他成员重叠布局(虽然这种情况较少见,主要取决于编译器的具体实现),或者仅仅是像往常一样存储。 限制:如果同一个类中有两个相同类型的成员都标记了此属性,它们不能共享同一个地址(必须能区分彼此)。 #### 1.10.2 举例说明 示例1:消除空成员的开销。 ```cpp struct Empty {}; // 空类 struct WithoutAttr { int i; Empty e; }; struct WithAttr { int i; [[no_unique_address]] Empty e; // 开启优化 }; void test() { cout << sizeof(Empty) << endl; // 1 cout << sizeof(WithoutAttr) << endl; //8 ,(4字节int + 1字节Empty + 3字节padding) cout << sizeof(WithAttr) << endl; //8,(VS2026输出8,理想状态:Empty被优化为0字节,输出 4) } ``` 示例2:优化自定义分配器或哈希函数 在编写容器或工具类时,我们经常需要持有一个分配器或哈希函数对象。大多数默认的分配器(如 std::allocator)都是无状态的空类,此时就可以将 Alloc 加上\[\[no_unique_address\]\]修饰。 ```cpp template > class MyVector { T* data; size_t size; // C++20 之前:为了省空间,可能需要私有继承 Alloc // C++20:直接作为成员,加上属性即可 [[no_unique_address]] Alloc allocator; public: // ... }; ```

相关推荐
Mr_WangAndy3 小时前
C++20新特性_模块(Modules)
c++20·c++40周年·c++20新特性模块
Mr_WangAndy4 小时前
C++20新特性_范围 `for` 循环的初始化语句
c++20·c++40周年·范围for初始化
Mr_WangAndy4 小时前
C++20新特性_三路比较运算符 (<=>)
c++20·c++40周年·三路比较运算符
Mr_WangAndy5 小时前
C++20新特性_consteval 和 constinit
c++20·c++40周年·consteval·constinit
Mr_WangAndy5 小时前
C++20新特性_协程(Coroutines)
c++20·c++20新特性·c++40周年·c++20协程
Mr_WangAndy5 小时前
C++20新特性_Lambda 改进
c++20·c++20新特性·c++40周年·lambda表达式改进
Mr_WangAndy15 小时前
C++17 新特性_第二章 C++17 语言特性_std::any和string_view
c++·string_view·c++40周年·c++17新特性·c++新特性any
Mr_WangAndy17 小时前
C++17 新特性_第一章 C++17 语言特性___has_include,u8字符字面量
c++·c++40周年·c++17新特性·__has_include·u8字面量
Mr_WangAndy1 天前
C++17 新特性_第二章 C++17标准库特性_std::invoke和std::invoke_result_t
c++·invoke·c++40周年·c++17新特性·invoke_result