gpt4 book ai didi

c++ - c++20 [[no_unique_address]] 中的新特性是什么?

转载 作者:行者123 更新时间:2023-12-01 08:31:01 26 4
gpt4 key购买 nike

我已经阅读了新的 c++20 特性 no_unique_address好几次,我希望如果有人能用一个比下面这个来自 c++ 引用的例子更好的例子来解释和说明。

Explanation Applies to the name being declared in the declaration of anon-static data member that's not a bit field.

Indicates that this data member need not have an address distinct fromall other non-static data members of its class. This means that if themember has an empty type (e.g. stateless Allocator), the compiler mayoptimise it to occupy no space, just like if it were an empty base. Ifthe member is not empty, any tail padding in it may be also reused tostore other data members.

#include <iostream>

struct Empty {}; // empty class

struct X {
int i;
Empty e;
};

struct Y {
int i;
[[no_unique_address]] Empty e;
};

struct Z {
char c;
[[no_unique_address]] Empty e1, e2;
};

struct W {
char c[2];
[[no_unique_address]] Empty e1, e2;
};

int main()
{
// e1 and e2 cannot share the same address because they have the
// same type, even though they are marked with [[no_unique_address]].
// However, either may share address with c.
static_assert(sizeof(Z) >= 2);

// e1 and e2 cannot have the same address, but one of them can share with
// c[0] and the other with c[1]
std::cout << "sizeof(W) == 2 is " << (sizeof(W) == 2) << '\n';
}
  • 有人可以向我解释这个功能背后的目的是什么,我应该什么时候使用它?
  • e1 和 e2 不能有相同的地址,但其中一个可以与 c[0] 共享,另一个与 c[1] 共享,有人可以解释吗?为什么我们会有这种关系?
  • 最佳答案

    该功能背后的目的与您的引述中所述完全相同:“编译器可能会对其进行优化以使其不占用空间”。这需要两件事:

  • 一个空的对象。
  • 想要拥有可能为空类型的非静态数据成员的对象。

  • 第一个非常简单,您使用的引用甚至说明了它是一个重要的应用程序。 std::allocator 类型的对象实际上不存储任何东西。它只是一个基于类的接口(interface)进入全局 ::new::delete内存分配器。不存储任何类型数据(通常通过使用全局资源)的分配器通常称为“无状态分配器”。
    需要分配器感知容器来存储用户提供的分配器的值(默认为该类型的默认构造的分配器)。这意味着容器必须具有该类型的子对象,该子对象由用户提供的分配器值初始化。那个子对象占用空间......理论上。
    考虑 std::vector .这种类型的常见实现是使用 3 个指针:一个用于数组的开头,一个用于数组有用部分的结尾,一个用于数组的已分配块的结尾。在 64 位编译中,这 3 个指针需要 24 字节的存储空间。
    无状态分配器实际上没有任何要存储的数据。但是在 C++ 中,每个对象的大小至少为 1。所以如果 vector存储一个分配器作为成员,每个 vector<T, Alloc>即使分配器不存储任何内容,也必须至少占用 32 个字节。
    对此的常见解决方法是派生 vector<T, Alloc>来自 Alloc本身。原因是基类子对象的大小不需要为 1。如果基类没有成员且没有非空基类,则允许编译器在派生类中优化基类的大小实际上不占用空间。这称为“空基优化”(标准布局类型需要它)。
    因此,如果您提供无状态分配器,则 vector<T, Alloc>从这个分配器类型继承的实现仍然只有 24 个字节的大小。
    但是有一个问题:你必须从分配器继承。这真的很烦人。而且很危险。首先,分配器可以是 final ,这实际上是标准允许的。其次,分配器可能有干扰 vector 的成员。的成员。第三,这是人们必须学习的习语,这使它成为 C++ 程序员的民间智慧,而不是任何人都可以使用的明显工具。
    因此,虽然继承是一种解决方案,但它并不是一个很好的解决方案。
    这是什么 [[no_unique_address]]是为了。它允许容器将分配器存储为成员子对象而不是基类。如果分配器为空,则 [[no_unique_address]]将允许编译器使其不占用类定义中的空间。所以这样一个 vector大小仍可能为 24 字节。

    e1 and e2 cannot have the same address, but one of them can share with c[0] and the other with c1 can some one explain? why do we have such kind of relation ?


    C++ 有一个基本规则,它的对象布局必须遵循。我称之为“ unique identity rule”。
    对于任何两个对象,至少必须满足以下一项:
  • 他们必须有不同的类型。
  • 它们在内存中必须有不同的地址。
  • 它们实际上必须是同一个对象。
  • e1e2不是同一个对象,因此违反了#3。它们也共享相同的类型,因此违反了 #1。因此,他们必须遵循#2:他们不能有相同的地址。在这种情况下,由于它们是相同类型的子对象,这意味着该类型的编译器定义的对象布局不能在对象内给它们相同的偏移量。 e1c[0]是不同的对象,所以 #3 再次失败。但它们满足#1,因为它们有不同的类型。因此(遵循 [[no_unique_address]] 的规则)编译器可以将它们分配给对象内的相同偏移量。 e2 也是如此和 c[1] .
    如果编译器想要将一个类的两个不同成员分配给包含对象内的相同偏移量,那么它们必须是不同的类型(请注意,这是通过它们的所有子对象递归的)。因此,如果它们具有相同的类型,则它们必须具有不同的地址。

    关于c++ - c++20 [[no_unique_address]] 中的新特性是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62784750/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com