具有引用成员的结构是否具有唯一的对象表示?
Does struct with reference member have unique object representation?
这 提出了以下问题。
假设我们有一个简单的
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 之间的分歧
这是 Clang 的故意解释
请注意,Clang 明确选择了他们的方法 based on comments from Richard Smith,即使知道 GCC 被拒绝(在 OP 的上下文中)std::has_unique_object_representations_v<S>
并将此 GCC 行为指出为可能的错误 [强调我的]:
erichkeane References are not trivially copyable, so they will prevent the struct from having a unique object representation.
rsmith That sounds like the wrong behavior to me. If two structs have references that bind to the same object, then they have the same object representation, so the struct does have unique object representations.
erichkeane I didn't think of it that way... I WILL note that GCC rejects references in their implementation, but that could be a bug on their part.
rsmith [...] So I think references, like pointers, should always be considered to have unique object representations when considered as members of objects of class type. (But __has_unique_object_representations(T&)
should still return false
because T&
is not a trivially-copyable type, even though a class containing a T&
might be.)
正如@idclev 463035818 所指出的,Clang 和 GCC agree that S
is trivially copyable, meaning their disagreement lies in whether two objects of (trivially copyable) type S
with the same value have the same object representation. For an answer to the latter, refer to (Clang 在技术上是错误的,而相关的标准段落值得商榷)。
S
可简单复制,因为它没有 class 类型的成员,也没有用户声明的成员函数。这是没有争议的;正如 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是错误的。
首先,引用不是对象。对象在 [intro.object] 中指定,引用在 [dcl.ref].
中指定
子对象是对象 ([intro.object])。因此引用成员不是子对象,因此 class 只包含引用成员(没有基数)没有子对象(即使它有数据成员)。
[meta.unary.prop]
The predicate condition for a template specialization has_unique_object_representations shall be satisfied if and only if:
- T is trivially copyable, and
- any two objects of type T with the same value have the same object representation, where two objects of array or non-union class type are considered to have the same value if their respective sequences of direct subobjects have the same values, ...
子对象序列为空,因此等于另一个空序列,因此类型 S
的所有对象都具有“相同值”2 根据此规则。
但是,引用不同对象的对象必然具有不同的对象表示。所以第二个要求不1满足
因此对象表示不是唯一的,Clang 在技术上是错误的,而 GCC 和 MSVC(与 GCC 的结果相同)是正确的。
如果我们得出不满足第二个要求的结论,这就变得1 有点跑题了,但是:S
是否可以简单复制?
static_assert(std::is_trivially_copyable_v<S>);
在 Clang 和 GCC 中均通过,但根据 MSVC,S
不可 简单复制。那么,哪个是正确的?
[class.copy.ctor]
A copy/move constructor for class X is trivial if it is not user-provided and if:
- class X has no virtual functions ([class.virtual]) and no virtual base classes ([class.mi]), and
- the constructor selected to copy/move each direct base class subobject is trivial, and
- for each non-static data member of X that is of class type (or array thereof), the constructor selected to copy/move that member is trivial;
这些都很满意。因此 S
有一个简单的 copy/move 构造函数。
[class.prop]
A trivially copyable class is a class:
- that has at least one eligible copy constructor, move constructor, copy assignment operator, or move assignment operator ([special], [class.copy.ctor], [class.copy.assign]),
- where each eligible copy constructor, move constructor, copy assignment operator, and move assignment operator is trivial, and
- that has a trivial, non-deleted destructor ([class.dtor]).
所有都满足,因此 S
是可简单复制的,而 MSVC 类型特征是错误的,相反。
1 编辑:我原来的结论倒过来了。
2 在考虑 class 对象的“值”时,引用数据成员 是否应该 被忽略我的意见值得商榷。这种忽略它们的技术性可能被认为是标准中的缺陷。
这
假设我们有一个简单的
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 之间的分歧这是 Clang 的故意解释
请注意,Clang 明确选择了他们的方法 based on comments from Richard Smith,即使知道 GCC 被拒绝(在 OP 的上下文中)std::has_unique_object_representations_v<S>
并将此 GCC 行为指出为可能的错误 [强调我的]:
erichkeane References are not trivially copyable, so they will prevent the struct from having a unique object representation.
rsmith That sounds like the wrong behavior to me. If two structs have references that bind to the same object, then they have the same object representation, so the struct does have unique object representations.
erichkeane I didn't think of it that way... I WILL note that GCC rejects references in their implementation, but that could be a bug on their part.
rsmith [...] So I think references, like pointers, should always be considered to have unique object representations when considered as members of objects of class type. (But
__has_unique_object_representations(T&)
should still returnfalse
becauseT&
is not a trivially-copyable type, even though a class containing aT&
might be.)
正如@idclev 463035818 所指出的,Clang 和 GCC agree that S
is trivially copyable, meaning their disagreement lies in whether two objects of (trivially copyable) type S
with the same value have the same object representation. For an answer to the latter, refer to
S
可简单复制,因为它没有 class 类型的成员,也没有用户声明的成员函数。这是没有争议的;正如 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是错误的。
首先,引用不是对象。对象在 [intro.object] 中指定,引用在 [dcl.ref].
中指定子对象是对象 ([intro.object])。因此引用成员不是子对象,因此 class 只包含引用成员(没有基数)没有子对象(即使它有数据成员)。
[meta.unary.prop]
The predicate condition for a template specialization has_unique_object_representations shall be satisfied if and only if:
- T is trivially copyable, and
- any two objects of type T with the same value have the same object representation, where two objects of array or non-union class type are considered to have the same value if their respective sequences of direct subobjects have the same values, ...
子对象序列为空,因此等于另一个空序列,因此类型 S
的所有对象都具有“相同值”2 根据此规则。
但是,引用不同对象的对象必然具有不同的对象表示。所以第二个要求不1满足
因此对象表示不是唯一的,Clang 在技术上是错误的,而 GCC 和 MSVC(与 GCC 的结果相同)是正确的。
如果我们得出不满足第二个要求的结论,这就变得1 有点跑题了,但是:S
是否可以简单复制?
static_assert(std::is_trivially_copyable_v<S>);
在 Clang 和 GCC 中均通过,但根据 MSVC,S
不可 简单复制。那么,哪个是正确的?
[class.copy.ctor]
A copy/move constructor for class X is trivial if it is not user-provided and if:
- class X has no virtual functions ([class.virtual]) and no virtual base classes ([class.mi]), and
- the constructor selected to copy/move each direct base class subobject is trivial, and
- for each non-static data member of X that is of class type (or array thereof), the constructor selected to copy/move that member is trivial;
这些都很满意。因此 S
有一个简单的 copy/move 构造函数。
[class.prop]
A trivially copyable class is a class:
- that has at least one eligible copy constructor, move constructor, copy assignment operator, or move assignment operator ([special], [class.copy.ctor], [class.copy.assign]),
- where each eligible copy constructor, move constructor, copy assignment operator, and move assignment operator is trivial, and
- that has a trivial, non-deleted destructor ([class.dtor]).
所有都满足,因此 S
是可简单复制的,而 MSVC 类型特征是错误的,相反。
1 编辑:我原来的结论倒过来了。
2 在考虑 class 对象的“值”时,引用数据成员 是否应该 被忽略我的意见值得商榷。这种忽略它们的技术性可能被认为是标准中的缺陷。