[[no_unique_address]] and two member values of the same type

tom*_*tom 7 c++ struct memory-layout object-identity c++20

I'm playing around with [[no_unique_address]] in c++20.

In the example on cppreference we have an empty type Empty and type Z

struct Empty {}; // empty class

struct Z {
    char c;
    [[no_unique_address]] Empty e1, e2;
};
Run Code Online (Sandbox Code Playgroud)

Apparently, the size of Z has to be at least 2 because types of e1 and e2 are the same.

However, I really want to have Z with size 1. This got me thinking, what about wrapping Empty in some wrapper class with extra template parameter that enforces different types of e1 and e2.

template <typename T, int i>
struct Wrapper : public T{};

struct Z1 {
    char c;
    [[no_unique_address]] Wrapper<Empty,1> e1;
    [[no_unique_address]] Wrapper<Empty,2> e2;
};
Run Code Online (Sandbox Code Playgroud)

Unfortunately, sizeof(Z1)==2. Is there a trick to make size of Z1 to be one?

I'm testing this with gcc version 9.2.1 and clang version 9.0.0


In my application, I have lots of empty types of the form

template <typename T, typename S>
struct Empty{
    [[no_unique_address]] T t;
    [[no_unique_address]] S s;
};
Run Code Online (Sandbox Code Playgroud)

Which is an empty type if T and S are also empty types and distinct! I want this type to be empty even if T and S are the same types.

Nic*_*las 7

如果T并且S也是空类型并且是不同的,那么哪个是空类型!即使TS是相同的类型,我也希望这种类型为空。

你不能得到那个。从技术上讲,即使TS是不同的空类型,您甚至不能保证它会是空的。记住:no_unique_address是一个属性;它隐藏对象的能力完全取决于实现。从标准的角度来看,您不能强制执行空对象的大小。

随着 C++20 实现的成熟,您应该假设它[[no_unique_address]]通常遵循空基优化的规则。也就是说,只要相同类型的两个对象不是子对象,您就可能会隐藏起来。但在这一点上,这是一种运气。

至于特定情况TS是相同的类型,即根本不可能的。尽管名称“no_unique_address”有含义,但实际情况是 C++ 要求,给定两个指向相同类型对象的指针,这些指针要么指向同一个对象,要么具有不同的地址。我称之为“唯一身份规则”,no_unique_address并不影响它。来自[intro.object]/9

如果一个对象嵌套在另一个对象中,或者如果至少一个对象是零大小的子对象并且它们属于不同类型,那么两个生命周期重叠且不是位字段的对象可能具有相同的地址;否则,它们具有不同的地址并占用不相交的存储字节。

声明[[no_unique_address]]为零大小的空类型的成员,但具有相同的类型使这成为不可能。

确实,仔细想想,试图通过嵌套来隐藏空类型仍然违反了唯一标识规则。考虑你WrapperZ1情况。给定 az1是 的一个实例Z1,很明显z1.e1z1.e2是不同类型的不同对象。但是,z1.e1不嵌套,z1.e2反之亦然。虽然他们有不同的类型,(Empty&)z1.e1并且(Empty&)z1.e2没有不同的类型。但它们确实指向不同的对象。

并且根据唯一身份规则,它们必须具有不同的地址。因此,即使e1e2名义上是不同的类型,它们的内部结构也必须遵守针对同一包含对象中的其他子对象的唯一标识。递归。

无论您如何尝试,您想要的东西在目前的 C++ 中都是不可能的。