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.