具有引用成员的结构是否具有唯一的对象表示?

Evg*_*Evg 30 c++ language-lawyer c++17

这个答案提出了以下问题。

假设我们有一个简单的

struct S {
    int& i;
}
Run Code Online (Sandbox Code Playgroud)

内部(至少在 GCC 和 Clang 中)S只包含一个指向 an 的指针int,并且

static_assert(sizeof(int*) == 8);
static_assert(sizeof(S)    == 8);
Run Code Online (Sandbox Code Playgroud)

是否S有唯一的对象表示?GCC 和 Clang 不同意 *:

static_assert( std::has_unique_object_representations_v<int*>);
static_assert(!std::has_unique_object_representations_v<S>);    // GCC
static_assert( std::has_unique_object_representations_v<S>);    // Clang
Run Code Online (Sandbox Code Playgroud)

哪个编译器就在这里,为什么?

* idclev 463035818指出 GCC 和 Clang 之间存在分歧

eer*_*ika 17

首先,引用不是对象。对象在 [intro.object] 中指定,引用在 [dcl.ref] 中。

子对象是对象([intro.object])。因此引用成员不是子对象,因此只包含引用成员(没有基类)的类没有子对象(即使它有数据成员)。

[meta.unary.prop]

模板特化 has_unique_object_representations 的谓词条件应满足当且仅当

  • T 是可简单复制的,并且
  • 具有相同值的任何两个类型 T 的对象具有相同的对象表示,其中数组或非联合类类型的两个对象如果它们各自的直接子对象序列具有相同的值,则被认为具有相同的值,...

子对象的序列是空的,因此等于另一个空序列,因此根据此规则,所有类型的对象S都具有“相同的值” 2

但是,引用不同对象的对象必然具有不同的对象表示。因此,第二个要求不是1满足。

因此对象表示不是唯一的,Clang 在技术上是错误的,而 GCC 和 MSVC(与 GCC 具有相同的结果)是正确的。



这已经成为1位题外话,如果我们认为第二个要求不被满足,但:是S平凡可复制?

static_assert(std::is_trivially_copyable_v<S>);
Run Code Online (Sandbox Code Playgroud)

通过在这两个铛和GCC,但根据MSVC,S平凡的可复制。那么,哪个是正确的?

[class.copy.ctor]

类 X 的复制/移动构造函数如果不是用户提供的并且如果:

  • 类 X 没有虚函数([class.virtual])和虚基类([class.mi]),并且
  • 选择复制/移动每个直接基类子对象的构造函数是微不足道的,并且
  • 对于 X 的每个类类型(或其数组)的非静态数据成员,选择复制/移动该成员的构造函数是微不足道的;

这些都满足了。因此S有一个简单的复制/移动构造函数。

[类.prop]

一个简单可复制的类是一个类:

  • 具有至少一个有资格的拷贝构造,移动构造函数,复制赋值运算符,移动赋值运算符([特殊],[class.copy.ctor],[class.copy.assign]),
  • 其中每个符合条件的复制构造函数、移动构造函数、复制赋值运算符和移动赋值运算符都是微不足道的,并且
  • 它有一个简单的、未删除的析构函数([class.dtor])。

一切都得到满足,因此S 可以简单地复制,而 MSVC 类型特征相反地陈述是错误的。


1编辑:我最初得到了反向结论。

2在考虑类对象的“值”时是否应该忽略引用数据成员在我看来是有争议的。这种忽略它们的技术性可能被视为标准中的缺陷。


dfr*_*fri 16

这是 Clang 的有意解释

请注意,Clang根据 Richard Smith 的评论明确选择了他们的方法,即使知道 GCC 被拒绝(在 OP 的上下文中)std::has_unique_object_representations_v<S>并指出这种 GCC 行为可能是一个错误 [强调我的]:

erichkeane引用不是简单的可复制的,因此它们将阻止结构具有唯一的对象表示。

rsmith这对我来说听起来像是错误的行为。如果两个结构具有绑定到同一对象的引用,则它们具有相同的对象表示,因此该结构确实具有唯一的对象表示。

erichkeane我不这么认为...我会注意到GCC 在他们的实现中拒绝引用,但这可能是他们的一个错误。

rsmith [...] 所以我认为引用,就像指针一样,当被视为类类型对象的成员时应该总是被认为具有唯一的对象表示。(但__has_unique_object_representations(T&)仍应返回,false因为T&它不是可简单复制的类型,即使包含 a 的类T&可能是。)

正如@idclev 463035818 所指出的那样,Clang 和 GCC都同意S是普通可复制的,这意味着他们的分歧在于S具有相同值的(普通可复制)类型的两个对象是否具有相同的对象表示。对于后者的答案,请参阅@eerorika 的精彩论点(Clang 在技术上是错误的,而相关的标准段落是有争议的)。


MSa*_*ers 5

S可以简单地复制,因为它没有类类型的成员,也没有用户声明的成员函数。这是没有争议的;正如 idclev 463035818 指出两个编译器都同意std::is_trivially_copyable_v<S>==true

所以问题简化为两个相同的 S 对象是否按位相同。由于两种实现都选择将引用表示为指针(有效的选择),std::has_unique_object_representations_v<S>因此必须匹配std::has_unique_object_representations_v<int*>. 因此,GCC 是错误的。