Path: csiph.com!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: Tim Rentsch
Newsgroups: comp.lang.c++
Subject: Re: C++20 concepts rocks
Date: Mon, 07 Feb 2022 12:16:16 -0800
Organization: A noiseless patient Spider
Lines: 156
Message-ID: <86leymiga7.fsf@linuxsc.com>
References: <87k0easj5d.fsf@bsb.me.uk> <865yptlpgf.fsf@linuxsc.com> <87mtj4qd0h.fsf@bsb.me.uk> <86fsovkdjw.fsf@linuxsc.com> <877da7r7ap.fsf@bsb.me.uk> <86bkzjjs3c.fsf@linuxsc.com> <87k0e6q4j8.fsf@bsb.me.uk> <8635kukb17.fsf@linuxsc.com> <878rumptie.fsf@bsb.me.uk>
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Injection-Info: reader02.eternal-september.org; posting-host="792c2a82c28252afd7e8a94f82466189"; logging-data="11516"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18udEddclw8a6DuyHiJSnyweEDLSPT0xOY="
User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux)
Cancel-Lock: sha1:Wd/XgQ7mqof/ChcrszsjvUXIEGQ= sha1:WnyK2iGDQvSOpDi1OzKLRD8jEIc=
Xref: csiph.com comp.lang.c++:82962
Ben Bacarisse writes:
> Tim Rentsch writes:
>
>> Ben Bacarisse writes:
>>
>>> Tim Rentsch writes:
>>>
>>>> Ben Bacarisse writes:
>>
>> [.. considering the idiom (&x)[1], where x is an array ..]
>>
>>>>> So given
>>>>>
>>>>> int i;
>>>>> char *cp = (void *)(&i + 1);
>>>>>
>>>>> accessing the bytes of i from cp is also undefined.
>>>>
>>>> No, accessing cp[-1], etc, is defined behavior. The two
>>>> situations are not analogous. The reason is that in this case
>>>> there is only one array, without any subarrays.
>>>
>>> Does that not depend how literally one takes the "an object is an
>>> array of length one" rule?
>>>
>>> (I'm not being 100% serious here. It's obviously intended to
>>> mean, "an object is the sole element in an array of length one".)
>>
>> I believe the rule about treating objects as an array of length
>> one does not enter into the question;
>
> I thought it comes into play every time arithmetic is done on a
> pointer to an object that is not an element of an array. There
> appears to be no other explicit justification for the "one past the
> end" pointer &i + 1.
Yes, it is important that the expression &i+1 obeys the rule
about treating pointers to objects not in an array the same as if
the object were an element in an array of length 1. What I meant
was that this rule doesn't matter for whether there is undefined
behavior -- the expression &x + 1 always works, no matter what x
is (assuming the expression x doesn't refer to a bitfield or
something like that).
> Mind you, footnote 76 makes it clear: "an object that is not an
> array element is considered to belong to a single-element array for
> this purpose" and C has similar wording. I misremembered the rule:
> it's not that the object is considered to /be/ an array but to be
> /in/ an array or length one. I think the rule of often misquoted.
Looking up the relevant passages in the two standards, I find
they are slightly different. In C++ the rule is primarily about
objects, whereas in C the rule centers around pointer values.
Probably the two rules have the same consequences, but I haven't
tried to verify that.
>> all that matters is the
>> types involved. The type of &i is pointer to int. However, if
>> we did this
>>
>> int i;
>> char *cp = ( (char (*)[sizeof i]) &i )[1];
>>
>> then there would again be undefined behavior, because now the
>> type of the expression before the [1] is a pointer-to-array type,
>> and so that expression ultimately gets converted to a pointer to
>> an element of the second subarray.
>>
>>> I've cut the rest of your very helpful explanation except for a
>>> detail:
>>>
>>>> If you don't mind using a cast or compound literal, you can use
>>>> the &x+1 form regardless of whether x is a scalar or an array:
>>>>
>>>> char result[20];
>>>> char *ep = (void*){ &result + 1 };
>>>
>>> We're are talking about C++ here. Maybe there is some default
>>> constructor equivalent of this.
>>
>> Sorry, yes. In some cases in C, and in C++, using a cast may be
>> the only option:
>>
>> char result[20];
>> char *ep = (char*)( &result + 1 );
>
> Thinking about this a bit more I not 100% convinced; or perhaps I am
> just a little uneasy about how easy it would be to get this wrong.
>
> Switching to int (so that any special rules about accessing an object's
> byte representation using a pointer to char don't come into play) this
>
> int result[20];
> int *ip = (int *)(&result + 1);
>
> allows accesses like ip[-1], so (unless I'm missing some other rules)
> this
>
> int result2[2][20];
> int *ip = (int *)(&result2[0] + 1);
>
> allows access to result2[0] via negative indexes, but not to result2[1]
> using positive ones, despite the fact that the pointer being converted
> is a pointer to the second subarray of 'result2'. Is that how you see
> things?
If you think you're going to confuse with these questions, well
it is quite possible that you will. :)
Having said that, I press on.
Certainly the expression 'result2' is able to access any object
within the confines of the two-dimensional array result2.
Similarly the expression 'result2 + 0' is able to access any
object with in the confines of the two-dimensional array result2,
because 'result2 + 0' is exactly the same pointer as 'result2'.
The expression '&result2[0]' is, by definition, the same as the
expression '& * (result2 + 0)'. (Sidebar: in C that is always
true, whereas in C++ things may be different because of operator
overloading or something like that. I am assuming that these
possibilities don't come into play here, and '&result2[0]' acts
the same way in C++ as it does in C. (End sidebar.))
In C there is a rule that &*(E) is the same as (E) as long as the
constraints for the two operators are met. So in C '&result2[0]'
gives the same results as just 'result2', which is to say it is able
to access any object within the confines of the two-dimensional
array result2.
Incidentally this observation agrees with my usual practice when
calling functions taking a pointer parameter. If at a call site
I want to indicate that the argument is a pointer to a single
element I use an array indexing form, eg, &p[k]. Conversely if
I want to indicate that the argument is a pointer to an element
within a larger array I use an addition form, eg, p+k. I know
that both of these argument expressions allow access to the
entire array, and so are fully interchangeable as far as the
semantics goes; however I think it is useful to distinguish the
two intended meanings, so I normally use different argument forms
in the two cases.
Returning to your question, the expression '&result2[0]+1' is, in
C, the same as 'result2+1', which allows access to all of result2.
In C++, there is AFAICT no rule paralleling the '&*' rule in C.
However, looking at paragraph 3.2 in section 7.6.2.1 (in the C++
document N4860), I believe the same result obtains. The question
is more complicated in C++ because there might be overloading of
operator*(), and perhaps also of operator&(), but presumably those
possibilities aren't in play here. So, I believe that C++ gives
the same result that C does, allowing access to all of result2,
with the disclaimer that my knowledge and understanding of C++
is not what I would call authoritative or necessarily reliable.