This answer raised the following question.
Suppose we have a simple
struct S {
int& i;
}
Internally (in GCC and Clang, at least) S
contains just a pointer to an int
, and
static_assert(sizeof(int*) == 8);
static_assert(sizeof(S) == 8);
Does S
have a unique object representation? GCC and Clang disagree *:
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
Which compiler is right here and why?
* Disagreement between GCC and Clang was noted by idclev 463035818.
Firstly, references are not objects. Objects are specified in [intro.object] and references in [dcl.ref].
Subobjects are objects ([intro.object]). Therefore reference members are not subobjects and therefore a class containing only reference members (and no bases) has no subobjects (even though it has data members).
[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, ...
The sequence of subobjects is empty, and therefore equal to another empty sequence and thus all objects of type S
have the "same value"2 according to this rule.
However, objects that refer to different objects will necessarily have a different object representation. Therefore the second requirement is not1 satisified.
Therefore the object representation is not unique, and Clang is technically wrong and GCC and MSVC (which has same result as GCC) are right.
This has become1 a bit off topic if we conclude that second requirement isn't satisfied, but: Is S
trivially copyable?
static_assert(std::is_trivially_copyable_v<S>);
Passes in both Clang and GCC, but according to MSVC, S
is not trivially copyable. So, which is correct?
[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;
All of these are satisfied. Therefore S
has a trivial copy/move constructor.
[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]).
All are satisfied and therefore S
is trivially copyable, and MSVC type trait is wrong to state the opposite.
1 Edit: I originally got the conclusion backwards.
2 Whether reference data members should be ignored or not when considering "value" of a class object is in my opinion debatable. This technicality of ignoring them could potentially be considered a defect in the standard.
Note that Clang explicitly choose their approach based on comments from Richard Smith, even knowing GCC rejected (in the context of the OP) std::has_unique_object_representations_v<S>
and pointing out this GCC behaviour as a possible bug [emphasis mine]:
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.)
As pointed out by @idclev 463035818, both Clang and 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 @eerorika's excellent argument (Clang is technically wrong, whereas the relevant standard passage is debatable).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With