1

The C++ Standard define the concept "dynamic type" of a glvalue as follows:

dynamic type

<glvalue> type of the most derived object (1.8) to which the glvalue denoted by a glvalue expression refers [Example: if a pointer (8.3.1) p whose static type is “pointer to class B” is pointing to an object of class D, derived from B (Clause 10), the dynamic type of the expression *p is “D.” References (8.3.2) are treated similarly. —end example ]

How is this definition interpreted if what the glvalue refers to is not the most derived object? Does it mean "type of the most derived object that contains the object to which the glvalue denoted by a glvalue expression refers"?

Another puzzle is about the 4th paragraph in 5.7 of the C++ Standard:

... If the pointer operand points to an element of an array object, ...

I want to ask whether this condition holds if the pointer operand points to a subobject of an element of an array object. As an example, if it does not hold, then the behavior in the following code is undefined, right?

D d[10];
B *p = d; //B is a base class of D
p += 2;   //undefined behavior?
Community
  • 1
  • 1
xskxzr
  • 12,442
  • 12
  • 37
  • 77

3 Answers3

1

The wording is clear. The most derived object is implied to be a complete object, a data member or an array element, i.e. it is not a base class subobject.

WG21/N4527

1.8 The C++ object model [intro.object]

2 Objects can contain other objects, called subobjects. A subobject can be a member subobject (9.2), a base class subobject (Clause 10), or an array element. An object that is not a subobject of any other object is called a complete object.

3 For every object x, there is some object called the complete object of x, determined as follows:

(3.1) — If x is a complete object, then x is the complete object of x.

(3.2) — Otherwise, the complete object of x is the complete object of the (unique) object that contains x.

4 If a complete object, a data member (9.2), or an array element is of class type, its type is considered the most derived class, to distinguish it from the class type of any base class subobject; an object of a most derived class type or of a non-class type is called a most derived object.

D d[10];
B *p = d; //B is a base class of D
p += 2;   //undefined behavior?

This has undefined behavior, undoubtedly. There are no additional rules about derived classes. And since every operand of the expression p += 2 is prvalue, no dynamic types of glvalues are concerned.

Edit: Note that dynamic types of prvalues are same as their static types.

Community
  • 1
  • 1
FrankHB
  • 2,297
  • 23
  • 19
  • Does the word mean the glvalue always refers to a most derived object? I still cannot understand the word clearly. – xskxzr Nov 07 '15 at 13:59
  • @Shenke A glvalue refers to the most derived object when the dynamic type is concerned. You still don't need to care about the most derived object sometimes, e.g. `*p` in `decltype(*p)` where `p` is a pointer to object. – FrankHB Nov 08 '15 at 06:19
  • 1
    Thanks. I think there may be some ambiguity for the verb "refer to". – xskxzr Nov 08 '15 at 07:15
0

"Question" might be a better word than "puzzle" here.

Anyway, regarding the piece of code you showed, that's pretty interesting. It's not undefined, as everything in that piece of code is completely defined by the standard. The result is not what you'd expect however, if you'd expect pointer arithmetic to do dynamic type recognition.

In particular, p will point 2 B sizes away from the start of the array, not 2 D sizes. Again, this is completely well defined. Accessing that memory however might not be well defined.

Blindy
  • 65,249
  • 10
  • 91
  • 131
  • Thanks, but the standard says "if both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined." If *p is not an element of an array, it will be considered to belong to a single-element array, so according to the "otherwise" case, the behavior is undefined. Or do you mean *p is not only the subobject of an element of an array, but also an element of array? Anyway, @R Sahu has a different answer with you. – xskxzr Nov 07 '15 at 04:51
0

How is this definition interpreted if what the glvalue refers to is not the most derived object?

If the glvalue refers to a valid object, it is ALWAYS the most derived object that was constructed, not necessarily the most derived type of the base type.

Example:

class Base {};
class Derived1 : public Base {};
class Derived2 : public Derived1 {};

Base* ptr = new Derived1;

*ptr refers to Derived1, not Derived2 since the object that was constructed is of type Derived1.

D d[10];
B *p = d; //B is a base class of D
p += 2;   //undefined behavior?

Yes, that is undefined behavior.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 1
    Thanks, but why does `*ptr` refer to `Derived1` rather than the `Base` subobject of the `Derived1` object? The standard about pointer conversions says "...the result of the conversion is a pointer to the base class subobject of the derived class object." – xskxzr Nov 07 '15 at 04:58
  • @Shenke, if you use it in a context where a reference is expected, i.e. `Base&`, then it still evaluates to a reference that corresponds to the `Derived1` object. If you use it in a context where an object is expected, i.e. `Base`, then you get just the `Base` sub-object of `Derived1`. – R Sahu Nov 07 '15 at 05:03
  • Why will it evaluate to a reference corresponding to the `Derived1` object if a reference is expected? I find the indirection description in the standard: "The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to **the object or function to which the expression points**." – xskxzr Nov 07 '15 at 05:19
  • @Shenke, the pointer points to an object of type `Derived1` even though the pointer type is `Base*`. – R Sahu Nov 07 '15 at 05:22
  • But as I said above, the standard about pointer conversions says "A prvalue of type 'pointer to cv D', where D is a class type, can be converted to a prvalue of type 'pointer to cv B', where B is a base class (Clause 10) of D...The result of the conversion is **a pointer to the base class subobject** of the derived class object." Here obviously a pointer conversion occurs. – xskxzr Nov 07 '15 at 05:27
  • @Shenke, it does. But the underlying object is still an object of the derived class type. – R Sahu Nov 07 '15 at 05:35
  • But I think the language in the standard should be precise. It says indirection gets the object (it does not require the object to be a complete object) to which the expression points, and `ptr` indeed points to the `Base` subobject of the `Derived1` object (though the underlying object is the `Derived1` object), so `*ptr` should refer to the `Base` subobject of the `Derived1` object. Is there something wrong in this reasoning? – xskxzr Nov 07 '15 at 05:47
  • I am sure there are lots of places in the standard where the language can be more precise. However, I don't think this is one of those places. It could be that it makes sense to me since I have been programming in C++ for a while. – R Sahu Nov 07 '15 at 05:53