首页 > 解决方案 > 具有引用成员的结构是否具有唯一的对象表示?

问题描述

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

假设我们有一个简单的

struct S {
    int& i;
}

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

static_assert(sizeof(int*) == 8);
static_assert(sizeof(S)    == 8);

是否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

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

* idclev 463035818注意到 GCC 和 Clang 之间的分歧。

标签: c++c++17language-lawyer

解决方案


首先,引用不是对象。对象在 [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>);

在 Clang 和 GCC 中都通过,但根据 MSVC,S不是简单可复制的。那么,哪个是正确的?

[class.copy.ctor]

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

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

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

[类.prop]

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

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

所有人都满意,因此S 可以轻松复制,而 MSVC 类型特征则相反。


1编辑:我最初的结论是倒过来的。

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


推荐阅读