C++ Logo

std-discussion

Advanced search

Re: Is [[indeterminate]] not applicable to data members?

From: Lénárd Szolnoki <cpp_at_[hidden]>
Date: Thu, 13 Mar 2025 20:20:56 +0000
On 13 March 2025 16:00:20 GMT, Brian Bi via Std-Discussion <std-discussion_at_[hidden]> wrote:
>On Thu, Mar 13, 2025 at 6:00 AM Jens Maurer via Std-Discussion <
>std-discussion_at_[hidden]> wrote:
>
>>
>>
>> On 13/03/2025 05.16, Yongwei Wu via Std-Discussion wrote:
>> > On Wed, 12 Mar 2025 at 10:46, Yongwei Wu <wuyongwei_at_[hidden] <mailto:
>> wuyongwei_at_[hidden]>> wrote:
>> >
>> > On Tue, 11 Mar 2025 at 23:11, Brian Bi <bbi5291_at_[hidden] <mailto:
>> bbi5291_at_[hidden]>> wrote:
>>
>> > I'm uncomfortable with the idea of a class author being able to
>> override the user's choice to *not* declare a local variable
>> [[indeterminate]], by placing [[indeterminate]] on the declaration of a
>> non-static data member and thus making part of the object's storage
>> indeterminate anyway. Note that even if it's just in a private member,
>> there will still be UB if the object is used as the source of `memcpy`.
>>
>> "memcpy" only works for trivially copyable types to start with
>> [basic.types.general] p2+p3.
>> But it does work on indeterminate bytes, and faithfully copies those
>> to the destination.
>>
>
>We provide certain explicit guarantees only in the case of trivially
>copyable types, but I do not think that it was intended that the result of
>memcpying a non-trivially-copyable class object to a char buffer produces a
>completely unspecified result. For example, consider
>
>#include <string.h>
>
>struct S {
> char c;
> S() = default;
> S(const S&) {} // S is not trivially copyable
>};
>static_assert(sizeof(S) == 1);
>
>int main() {
> S s{}; // value-initialization: s.c is zeroed
> char buf[1];
> memcpy(buf, &s, 1);
> return buf[0];
>}
>
>One might take the point of view that this program always returns 0, or
>perhaps that because `S` is not trivially copyable you don't get such a
>guarantee and instead some arbitrary value can be returned, but I don't
>think we should interpret the standard such that `buf[0]` is given a
>possibly-indeterminate value. That seems to defeat one of the purposes of
>value-initialization.
>
>Furthermore, note that there are possibly-legitimate use cases for using a
>non-trivially-copyable object as the source of `memcpy`. In Google Test,
>when an `EXPECT_EQ` assertion fails, the framework prints out the two
>values that were expected to be equal but were not. But if those values do
>not provide a suitable `operator<<` enabling them to be printed, then the
>framework instead prints out their object representations. My assumption as
>a user is that, from C++26 onward, if I have not marked my local variables
>`[[indeterminate]]`, then that operation will not cause UB.

It could for EXPECT_EQ(vec[0], vec[1]) if there are uninitialized members involved, as objects with dynamic storage duration don't get erroneous value.

Alternatively we could adopt something like P3530 and make EXPECT_EQ's fallback printer use that. As a bonus:

1. It will be defined even for EXPECT_EQ(vec[0], vec[1]), if there are uninitialized members.
2. You don't get the possible early termination caused by EB.

And then it would also stop being a reason for disallowing [[indeterminate]] non-static data members.

The example data structure presented in P3530 requires both an [[indeterminate]] data member and their proposed interface to be both efficient and correct.

>
>None of this, of course, prevents class authors from writing a constructor
>that would deliberately overwrite the possibly-erroneous initial storage
>bytes with indeterminate bytes, in which case my expectation as a user may
>be subverted. What I think is that sharp knives should look sharp, marking
>a class member `[[indeterminate]]` is a sharper knife than merely declaring
>a local variable `[[indeterminate]]`, and therefore, the mechanism to force
>a class member to have indeterminate value should look sharper than
>`[[indeterminate]]` does. It should be the constructor explicitly
>allocating an `[[indeterminate]]` local char buffer and then memcpying it
>over whatever is already in the member. Hopefully a good compiler can
>optimize this to remove the initial erroneous initialization.
>
>If we let people declare class members `[[indeterminate]]`, I am sure
>Yongwei will use this feature responsibly but I am less confident about
>everyone else. I think there will be some people who think "it is my
>responsibility to make my class as fast as possible for my users, so I will
>slap `[[indeterminate]]` on all the members" without thinking too hard
>about the implications.
>
>
>> For classes that have byte buffers or "scratch space", I would expect
>> that the class author ensures they can't be copied, or, if the scratch
>> space happens to contain an object, that object is copied as appropriate,
>> instead of relying on the default copy constructor. Thus, the class
>> isn't trivially copyable anyway, and thus "memcpy" doesn't apply.
>>
>> > When a user writes `[[indeterminate]]`, it is their intention to
>> exploit the potential efficiency of not initializing certain storage. When
>> I, as class author, write `[[indeterminate]]` on data members, it is my
>> intention (and responsibility) to exploit the efficiency and make sure I do
>> not make mistakes.
>>
>> [[indeterminate]] can't appear on data members at this time:
>> [dcl.attr.indet] p1.
>>
>> Jens
>>
>>
>> > If a reasonable use of my code requires users to write
>> `[[indeterminate]]`, it is an overburden for users. If they are cautious,
>> they will have to check the implementation details of my class, which
>> defeats the purpose of encapsulation.
>> >
>> >
>> > Furthermore, I have the concern that erroneous behaviour, as it is
>> currently specified, may hinder the adoption of C++26, as it may slow down
>> certain applications, */without a proper way of remedy/*. No, adding the
>> [[indeterminate]] attribute to class-type local objects is not a remedy,
>> but uglification. It is far too confusing to mark a class-type object with
>> proper constructors as [[indeterminate]].
>> >
>> > I personally think it should be addressed in a new proposal, or maybe a
>> revised P2795? Opinions?
>> >
>> > --
>> > Yongwei Wu
>> > URL: http://wyw.dcweb.cn/ <http://wyw.dcweb.cn/>
>> >
>>
>> --
>> Std-Discussion mailing list
>> Std-Discussion_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>>
>
>

Received on 2025-03-13 20:21:15