Open xmh0511 opened 2 years ago
The definition of v
creates a single object in this example. There are different ways to refer to that object, e.g. by saying v
or by saying *iptr
. Nothing to see here. (Yes, modifying *iptr
is undefined behavior because the object designated by v
is a const object, but that's not directly relevant here.)
The issue here is that, whether *iptr
can refer to the object denoted by v
depends on whether [basic.compound] p4.1 can work here. But whether a
and b
are the same object is not clearly defined. Specifically, in this case, a
is an object of type const int
while b
is an object of type int
. Or, do you mean the case at #3
falls into "Otherwise, the pointer value is unchanged by the conversion." since [basic.compound] p4.1 cannot work?
[expr.static.cast] p13 says:
Otherwise, if the original pointer value points to an object a, and there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible (6.8.3) with a, the result is a pointer to b. Otherwise, the pointer value is unchanged by the conversion.
We agree the original pointer points to the v object, of type "const int". And there is an object b of type "int" (ignoring cv-qualification), which is the same object as the v object, so the result is a pointer to that v object. In this example, there is only ever one object we're talking about, and I think it goes without definition that x is the same as x (for any x).
And there is an object b of type "int" (ignoring cv-qualification), which is the same object as the v object
How could an object of type int
and an object of type const int
be the same object? If it were, then iptr
would point to an object of type int(which is b)
the result is a pointer to b.
which means, any modification to that object is well-defined, because b
is not a const object([basic.type.qualifier] p1.1), as per [dcl.type.cv] p4. In my opinion, it is exactly that b
and a
are not the same object, hence they are not pointer-convertible, then iptr
still points to the object v
as per:
Otherwise, the pointer value is unchanged by the conversion.
Thus, any modification to *iptr
will be UB according to [dcl.type.cv] p4
"is exactly that b and a are not the same object"
That can't be, because there is only a single object. Either "b" doesn't exist at all ("there is an object b of type T"), or it exists and then it's the same as a.
This is the obscure point here. Informally, any object is unique, an object cannot be of both type int
and const int
. In other words, an object has its unique determined type. Any two objects of different types cannot potentially be the same object unless it is Schrödinger’s Cat.
"b" doesn't exist at all ("there is an object b of type T")
The requirement of the first branch is that:
there is an object b of type T
Where T
is int since we convert a prvalue of "pointer to cv1 void" to a prvalue of type “pointer to cv2 int”. The complete wording is
there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible with a
In short, there is an object b
of type int that is pointer-interconvertible with an object a
of type const int
. we define pointer-interconvertible as
Two objects a and b are pointer-interconvertible if
- they are the same object
- [...]
The key point is whether a
(an object of type const int) and b
(an object of type int) are the same object? If it were, iptr
will point to b
but again that b
is an object of type int
.
I don't see anything here creating a distinct int
object. It is two different lvalue expressions v
and *iptr
that denote the same object of type const int
, although *iptr
is of type int
(decltype(*iptr)
is int&
).
@frederick-vs-ja The key point is whether the conversion falls into
Otherwise, if the original pointer value points to an object a, and there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible with a, the result is a pointer to b.
or
Otherwise, the pointer value is unchanged by the conversion.
It is two different lvalue expressions v and *iptr that denote the same object of type const int
you seem to admit that the conversion should fall into the second branch.
Take more attention to [expr.unary.op] p1
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.
Assume the case falls into the first branch, that branch specifies that the result is a pointer to the object b
(where b is of type int
). Conversely, if the case falls into the second branch, since the pointer value remains no change, in other words, iptr
still points to the object v
(where v is an object of type const int
).
you seem to admit that the conversion should fall into the second branch.
When there is not a different b object, IIUC two branches are not distinguishable. Note the "ignoring cv-qualification".
Take more attention to [expr.unary.op] p1
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.
"An lvalue referring to the object" is not needed to have the type of that object.
Note the "ignoring cv-qualification".
The destination of this conversion is "prvalue of type pointer to int(T)" where T
is exact the unqualified-version type, there is no top-level cv-qualification; Moreover, even though the pointed type has cv-qualification, its top-level cv-qualification is denoted by symbol cv2
as per [basic.type.qualifier] p6. And thus, b
is said to be an object of type int
(again, there is no cv-qualification to be ignored).
"An lvalue referring to the object" is not needed to have the type of that object.
An lvalue certainly can have a different type as the type of the actual object to which the expression points. The correct interpretation to [expr.unary.op] p1 is stated in P1839. As I have said in the above comments, the original object a
is the object denoted by v
, which is an object of type const int
, which is created by the definition of v
. The b
here is specified to an object of type int
(since T is int). Any same object cannot both have different types.
two branches are not distinguishable
No, since b
is an object of type int
, if the first branch works, the result is a pointer to b, which is an object of type int.
again, there is no cv-qualification to be ignored
IIUC there is, as "ignoring cv-qualification" is applied to the phrase "there is an object b of type T
", which means that both the cv-qualification of the type of b and that of T
are ignored for such purpose.
which means that both the cv-qualification of the type of b and that of T are ignored for such purpose.
I don't understand what it means. Would you prefer to directly ignore the introduction of b
?
there is an object b of type T
Isn't T
is exactly the specified type of b
? Could you point out what T
is in the complete rule? T
is introduced in this sentence
A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T”, where T is an object type
In the above case, we convert the source
vptr
(pointer to void) to a prvalue of type "pointer to int"
What is the destination of the conversion? In this case, it is the prvalue of type pointer to cv2 int, Isn't it? So, T
is obviously int. Do you agree with that? So, the introduction of b
means there is an object of type T[with T = int].
Do you select to directly ignore the introduction of
b
?
No. How can I select to directly ignore the introduction of b
while I paste it?
Isn't
T
is exactly the specified type ofb
?
This is what I disagree. Given T
being int
in the example, my comprehension is that the condition "there is an object b of type T
(ignoring cv-qualification)" is transformed into "there is an object b whose type is int
, const int
, volatile int
, or const volatile int
".
So,
T
is obviouslyint
. Do you agree with that?
Totally agree for this.
my comprehension is that the condition "there is an object b of type T (ignoring cv-qualification)" is transformed into "there is an object b whose type is int, const int, volatile int, or const volatile int".
That is our divergence here. In other words, the ambiguity of this portion. I construe "ignore cv-qualification" for any type T
to ignore the top-level cv-qualification of T
. Specifically, for any type T
, its qualification-decomposition is cv U
(where n = 0), we only take U
, this is "ignoring cv-qualification".
If it were the meaning of what you said, we should take the similar manner as [over.match.funcs.general] p7 did
for any permissible type cv U, any cv2 U, cv2 U&, or cv2 U&& is also a permissible type.
Instead of saying ignore the cv-qualification. Such as [temp.param] p6
The top-level cv-qualifiers on the template-parameter are ignored when determining its type.
template<int const V>
void fun(){
decltype(V) i = 0; // ignore the cv-qualification
i = 1;
}
Even a more similar context, see [expr.cond] p4
if T1 and T2 are the same class type (ignoring cv-qualification) and T2 is at least as cv-qualified as T1, the target type is T2
which means make the qualification-decomposition of T1
is such that
cv1 U1 (where n = 0)
while the qualification-decomposition of T2
cv2 U2 (where n = 0)
It requires that U1
and U2
are the same class type. For example
struct C{int a;};
using CvT = C const;
template<class T>
struct Identity{};
int main(){
C E;
std::cout<< typeid(Identity<decltype(true? E: CvT{})>).name(); // Identity<C const>
}
I construe "ignore cv-qualification" for any type
T
to ignore the top-level cv-qualification ofT
When the standard compares 2 types and says «(ignoring cv-qualification)», in all places it means to ignore cv-qualification on both types, not only on that which is close to «(ignoring cv-qualification)».
in all places it means to ignore cv-qualification on both types, not only on that which is close to «(ignoring cv-qualification)».
That means the result would point to object b
, which is of type int
(the rule specifies the type of that object). Any modification to object b
is well-defined. However, the intent is any modification to the object located at the address should cause UB(similar to the case in [dcl.type.cv] p4). I think the result should be the (unchanged)original pointer value, which points to the const object, and any modification to the glvalue
obtained from the pointer value is UB.
That means the result would point to object
b
, which is of typeint
(the rule specifies the type of that object)
Where?
there is an object b of type T
T is the type specified in pointer to cv2 T, where the top-level cv-qualification of the pointed-to type is denoted by cv2
, thus T
is a cv-unqualified type.
[expr.static.cast] p13 states
[basic.compound] p4.1 states
Consider this example:
The pointer value of
vptr
points tov
as per [conv.ptr] p2, the result ofconst_cast<void*>(cvptr)
refers to the original entity(the same pointer value as vptr's) as per [expr.const.cast] p3. After figuring out whatvptr
is, we can pay more attention to#3
, which is the case specified in [expr.static.cast] p13 where the conversion is that converts a prvalue of "pointer to void" to a prvalue of type "pointer to int". In this case, the original pointer value points tov
(which is objecta
, whose type isint const
) while the expected objectb
is of typeint
. They have different types. So, the issue is whether two objects that have similar types but are not the same can be the same object? From the perspective of English, when we say two objects are the same, they should be identical in any respect at least.