Date: Thu, 13 Mar 2025 12:00:20 -0400
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.
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
>
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.
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
>
-- *Brian Bi*
Received on 2025-03-13 16:00:37