Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does struct with reference member have unique object representation?

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.

like image 242
Evg Avatar asked Jul 03 '20 11:07

Evg


2 Answers

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.

like image 106
eerorika Avatar answered Nov 12 '22 11:11

eerorika


This is an intentional interpretation from Clang

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 return false because T& is not a trivially-copyable type, even though a class containing a T& 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).

like image 16
dfrib Avatar answered Nov 12 '22 10:11

dfrib