Topic: Why is it required to have no private/protected


Author: Oleksandr Pikozh <o.pikozh@gmail.com>
Date: Thu, 11 Jan 2018 16:57:54 +0200
Raw View
Maybe I am missing something.

But from purely intuitive point of view, if this works=E2=80=A6

 =C2=A0=C2=A0=C2=A0 class C {
 =C2=A0=C2=A0=C2=A0 public:
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C1 i{1, 2}; ///<-=
-here
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
 =C2=A0=C2=A0=C2=A0 };

=E2=80=A6then these should also work:

* Example 1 "aggregate-initializing private members within the class=20
definition":

 =C2=A0=C2=A0=C2=A0 class C1 {
 =C2=A0=C2=A0=C2=A0 private:
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
 =C2=A0=C2=A0=C2=A0 public:
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C1 i{1, 2}; ///<-=
-here
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
 =C2=A0=C2=A0=C2=A0 };

* Example 2 "aggregate-initializing private members from a friend class":

 =C2=A0=C2=A0=C2=A0 class C2 {
 =C2=A0=C2=A0=C2=A0 public:
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 friend C2H;
 =C2=A0=C2=A0=C2=A0 private:
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
 =C2=A0=C2=A0=C2=A0 };
 =C2=A0=C2=A0=C2=A0 class C2H {
 =C2=A0=C2=A0=C2=A0 public:
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C2 i{1, 2}; ///<-=
-here
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
 =C2=A0=C2=A0=C2=A0 }

* Example 3 "aggregate-initializing protected members from a descendant=20
class":

 =C2=A0=C2=A0=C2=A0 class C3 {
 =C2=A0=C2=A0=C2=A0 protected:
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
 =C2=A0=C2=A0=C2=A0 };
 =C2=A0=C2=A0=C2=A0 class C3D: public C3 {
 =C2=A0=C2=A0=C2=A0 public:
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C3 i{1, 2}; ///<-=
-here
 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
 =C2=A0=C2=A0=C2=A0 }

I.e. it is reasonable that aggregate initialization cannot be performed=20
when it tries to affect unaccessible fields. And it is also reasonable=20
that aggregate initialization cannot be performed even when at least one=20
of the class fields is unaccessible. But why aggregate initialization is=20
blocked for classes whose all fields are accessible from the current=20
scope, I don't understand.

--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/373e0b37-90e5-2de9-249c-e2fb95d908fe%40gmail.com=
..

.


Author: Nicol Bolas <jmckesson@gmail.com>
Date: Thu, 11 Jan 2018 08:17:47 -0800 (PST)
Raw View
------=_Part_9063_1207158954.1515687467945
Content-Type: multipart/alternative;
 boundary="----=_Part_9064_446307079.1515687467946"

------=_Part_9064_446307079.1515687467946
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable



On Thursday, January 11, 2018 at 9:59:43 AM UTC-5, Oleksandr Pikozh wrote:
>
> Maybe I am missing something.=20
>
> But from purely intuitive point of view, if this works=E2=80=A6=20
>
>      class C {=20
>      public:=20
>          int f1, f2;=20
>          static void s() {=20
>              C1 i{1, 2}; ///<--here=20
>          }=20
>      };=20
>
> =E2=80=A6then these should also work:=20
>
> * Example 1 "aggregate-initializing private members within the class=20
> definition":=20
>
>      class C1 {=20
>      private:=20
>          int f1, f2;=20
>      public:=20
>          static void s() {=20
>              C1 i{1, 2}; ///<--here=20
>          }=20
>      };=20
>
> * Example 2 "aggregate-initializing private members from a friend class":=
=20
>
>      class C2 {=20
>      public:=20
>          friend C2H;=20
>      private:=20
>          int f1, f2;=20
>      };=20
>      class C2H {=20
>      public:=20
>          static void s() {=20
>              C2 i{1, 2}; ///<--here=20
>          }=20
>      }=20
>
> * Example 3 "aggregate-initializing protected members from a descendant=
=20
> class":=20
>
>      class C3 {=20
>      protected:=20
>          int f1, f2;=20
>      };=20
>      class C3D: public C3 {=20
>      public:=20
>          static void s() {=20
>              C3 i{1, 2}; ///<--here=20
>          }=20
>      }=20
>
> I.e. it is reasonable that aggregate initialization cannot be performed=
=20
> when it tries to affect unaccessible fields. And it is also reasonable=20
> that aggregate initialization cannot be performed even when at least one=
=20
> of the class fields is unaccessible. But why aggregate initialization is=
=20
> blocked for classes whose all fields are accessible from the current=20
> scope, I don't understand.
>

There is no technical reason why it has to be this way (though the recent=
=20
`is_aggregate` trait would be made non-useful). But it's still not a good=
=20
idea.

First, the ordering of members in different access classes is undefined. So=
=20
you would only be able to expand this to classes where NSDMs are all of the=
=20
same access classes.

Second, this wouldn't work in real code.

Consider `C3`. Why are its members private?

If you wanted a type that could hold any two integers, but you couldn't=20
change them once set, you would just declare them `const` and leave them=20
public. But you made them mutable. And private.

Therefore, we must assume that you want to control the values of these two=
=20
integers. To do that, you need to have a user-provided constructor. And=20
once you have that, the type isn't an aggregate anymore.

See, the word "aggregate" was not chosen arbitrarily. The concept of=20
"aggregate" is exactly what the word means=20
<http://www.dictionary.com/browse/aggregate>: a thing made entirely of=20
other things. An aggregate currently is an assemblage of objects, each of=
=20
which can have any particular value which that subobject can store.

If you have a user-provided constructor, then the type isn't a mere=20
aggregation of things. Its member subobjects cannot assume any value;=20
you're controlling what values are legal for your object. Therefore, once=
=20
you have a constructor, the type isn't an aggregate anymore.

And if you don't have a constructor for `C1`, why are its members private?=
=20
What are you doing with classes with private members that have no=20
constructors?

--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/9e80c7be-e2be-4945-a3ff-93886dd2b336%40isocpp.or=
g.

------=_Part_9064_446307079.1515687467946
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><br><br>On Thursday, January 11, 2018 at 9:59:43 AM UTC-5,=
 Oleksandr Pikozh wrote:<blockquote class=3D"gmail_quote" style=3D"margin: =
0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">Maybe =
I am missing something.
<br>
<br>But from purely intuitive point of view, if this works=E2=80=A6
<br>
<br>=C2=A0=C2=A0=C2=A0=C2=A0 class C {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 public:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C1 i{1, =
2}; ///&lt;--here
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
<br>=C2=A0=C2=A0=C2=A0=C2=A0 };
<br>
<br>=E2=80=A6then these should also work:
<br>
<br>* Example 1 &quot;aggregate-initializing private members within the cla=
ss=20
<br>definition&quot;:
<br>
<br>=C2=A0=C2=A0=C2=A0=C2=A0 class C1 {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 private:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
<br>=C2=A0=C2=A0=C2=A0=C2=A0 public:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C1 i{1, =
2}; ///&lt;--here
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
<br>=C2=A0=C2=A0=C2=A0=C2=A0 };
<br>
<br>* Example 2 &quot;aggregate-initializing private members from a friend =
class&quot;:
<br>
<br>=C2=A0=C2=A0=C2=A0=C2=A0 class C2 {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 public:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 friend C2H;
<br>=C2=A0=C2=A0=C2=A0=C2=A0 private:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
<br>=C2=A0=C2=A0=C2=A0=C2=A0 };
<br>=C2=A0=C2=A0=C2=A0=C2=A0 class C2H {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 public:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C2 i{1, =
2}; ///&lt;--here
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
<br>=C2=A0=C2=A0=C2=A0=C2=A0 }
<br>
<br>* Example 3 &quot;aggregate-initializing protected members from a desce=
ndant=20
<br>class&quot;:
<br>
<br>=C2=A0=C2=A0=C2=A0=C2=A0 class C3 {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 protected:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
<br>=C2=A0=C2=A0=C2=A0=C2=A0 };
<br>=C2=A0=C2=A0=C2=A0=C2=A0 class C3D: public C3 {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 public:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C3 i{1, =
2}; ///&lt;--here
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
<br>=C2=A0=C2=A0=C2=A0=C2=A0 }
<br>
<br>I.e. it is reasonable that aggregate initialization cannot be performed=
=20
<br>when it tries to affect unaccessible fields. And it is also reasonable=
=20
<br>that aggregate initialization cannot be performed even when at least on=
e=20
<br>of the class fields is unaccessible. But why aggregate initialization i=
s=20
<br>blocked for classes whose all fields are accessible from the current=20
<br>scope, I don&#39;t understand.<br></blockquote><div><br></div><div>Ther=
e is no technical reason why it has to be this way (though the recent `is_a=
ggregate` trait would be made non-useful). But it&#39;s still not a good id=
ea.</div><div><br></div><div>First, the ordering of members in different ac=
cess classes is undefined. So you would only be able to expand this to clas=
ses where NSDMs are all of the same access classes.</div><div><br></div><di=
v>Second, this wouldn&#39;t work in real code.</div><div><br></div><div>Con=
sider `C3`. Why are its members private?</div><div><br></div><div>If you wa=
nted a type that could hold any two integers, but you couldn&#39;t change t=
hem once set, you would just declare them `const` and leave them public. Bu=
t you made them mutable. And private.</div><div><br></div><div>Therefore, w=
e must assume that you want to control the values of these two integers. To=
 do that, you need to have a user-provided constructor. And once you have t=
hat, the type isn&#39;t an aggregate anymore.</div><div><br></div><div>See,=
 the word &quot;aggregate&quot; was not chosen arbitrarily. The concept of =
&quot;aggregate&quot; is exactly <a href=3D"http://www.dictionary.com/brows=
e/aggregate">what the word means</a>: a thing made entirely of other things=
.. An aggregate currently is an assemblage of objects, each of which can hav=
e any particular value which that subobject can store.</div><div><br></div>=
<div>If you have a user-provided constructor, then the type isn&#39;t a mer=
e aggregation of things. Its member subobjects cannot assume any value; you=
&#39;re controlling what values are legal for your object. Therefore, once =
you have a constructor, the type isn&#39;t an aggregate anymore.</div><div>=
<br></div><div>And if you don&#39;t have a constructor for `C1`, why are it=
s members private? What are you doing with classes with private members tha=
t have no constructors?</div></div>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/9e80c7be-e2be-4945-a3ff-93886dd2b336%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/9e80c7be-e2be-4945-a3ff-93886dd2b336=
%40isocpp.org</a>.<br />

------=_Part_9064_446307079.1515687467946--

------=_Part_9063_1207158954.1515687467945--

.


Author: Oleksandr Pikozh <o.pikozh@gmail.com>
Date: Thu, 11 Jan 2018 19:38:46 +0200
Raw View
This is a multi-part message in MIME format.
--------------87C8F21A4C1213DBD9276D21
Content-Type: text/plain; charset="UTF-8"; format=flowed
Content-Transfer-Encoding: quoted-printable



On 11.01.18 18:17, Nicol Bolas wrote:
>
>
> On Thursday, January 11, 2018 at 9:59:43 AM UTC-5, Oleksandr Pikozh=20
> wrote:
>
>     Maybe I am missing something.
>
>     But from purely intuitive point of view, if this works=E2=80=A6
>
>     =C2=A0=C2=A0=C2=A0=C2=A0 class C {
>     =C2=A0=C2=A0=C2=A0=C2=A0 public:
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C1 i{1=
, 2}; ///<--here
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
>     =C2=A0=C2=A0=C2=A0=C2=A0 };
>
>     =E2=80=A6then these should also work:
>
>     * Example 1 "aggregate-initializing private members within the class
>     definition":
>
>     =C2=A0=C2=A0=C2=A0=C2=A0 class C1 {
>     =C2=A0=C2=A0=C2=A0=C2=A0 private:
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
>     =C2=A0=C2=A0=C2=A0=C2=A0 public:
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C1 i{1=
, 2}; ///<--here
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
>     =C2=A0=C2=A0=C2=A0=C2=A0 };
>
>     * Example 2 "aggregate-initializing private members from a friend
>     class":
>
>     =C2=A0=C2=A0=C2=A0=C2=A0 class C2 {
>     =C2=A0=C2=A0=C2=A0=C2=A0 public:
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 friend C2H;
>     =C2=A0=C2=A0=C2=A0=C2=A0 private:
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
>     =C2=A0=C2=A0=C2=A0=C2=A0 };
>     =C2=A0=C2=A0=C2=A0=C2=A0 class C2H {
>     =C2=A0=C2=A0=C2=A0=C2=A0 public:
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C2 i{1=
, 2}; ///<--here
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
>     =C2=A0=C2=A0=C2=A0=C2=A0 }
>
>     * Example 3 "aggregate-initializing protected members from a
>     descendant
>     class":
>
>     =C2=A0=C2=A0=C2=A0=C2=A0 class C3 {
>     =C2=A0=C2=A0=C2=A0=C2=A0 protected:
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
>     =C2=A0=C2=A0=C2=A0=C2=A0 };
>     =C2=A0=C2=A0=C2=A0=C2=A0 class C3D: public C3 {
>     =C2=A0=C2=A0=C2=A0=C2=A0 public:
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C3 i{1=
, 2}; ///<--here
>     =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
>     =C2=A0=C2=A0=C2=A0=C2=A0 }
>
>     I.e. it is reasonable that aggregate initialization cannot be
>     performed
>     when it tries to affect unaccessible fields. And it is also
>     reasonable
>     that aggregate initialization cannot be performed even when at
>     least one
>     of the class fields is unaccessible. But why aggregate
>     initialization is
>     blocked for classes whose all fields are accessible from the current
>     scope, I don't understand.
>
>
> There is no technical reason why it has to be this way (though the=20
> recent `is_aggregate` trait would be made non-useful). But it's still=20
> not a good idea.
>
> First, the ordering of members in different access classes is=20
> undefined. So you would only be able to expand this to classes where=20
> NSDMs are all of the same access classes.
>
> Second, this wouldn't work in real code.
>
> Consider `C3`. Why are its members private?
>
> If you wanted a type that could hold any two integers, but you=20
> couldn't change them once set, you would just declare them `const` and=20
> leave them public. But you made them mutable. And private.
>
> Therefore, we must assume that you want to control the values of these=20
> two integers. To do that, you need to have a user-provided=20
> constructor. And once you have that, the type isn't an aggregate anymore.
>
> See, the word "aggregate" was not chosen arbitrarily. The concept of=20
> "aggregate" is exactly what the word means=20
> <http://www.dictionary.com/browse/aggregate>: a thing made entirely of=20
> other things. An aggregate currently is an assemblage of objects, each=20
> of which can have any particular value which that subobject can store.
>
> If you have a user-provided constructor, then the type isn't a mere=20
> aggregation of things. Its member subobjects cannot assume any value;=20
> you're controlling what values are legal for your object. Therefore,=20
> once you have a constructor, the type isn't an aggregate anymore.
>
> And if you don't have a constructor for `C1`, why are its members=20
> private? What are you doing with classes with private members that=20
> have no constructors?

Let me answer point-by-point.

> First, the ordering of members in different access classes is=20
> undefined. So you would only be able to expand this to classes where=20
> NSDMs are all of the same access classes.

Yes, undefined ordering of members in different access levels is a=20
problem. But it can be workarounded by less-strict restriction: if a=20
class has members of different access levels, allow it to be initialized=20
only with C++20 designated initializers (e.g. `A a{.x =3D 1, .y =3D2}`),=20
otherwise allow it to be initialized by both designated and=20
non-designated (e.g. `A a{1, 2}`) initializers. No need to forbid=20
aggregate initialization at all.

> Second, this wouldn't work in real code.
>
> Consider `C3`. Why are its members private?

Example C3 with protected members was purely theoretical (I provided it=20
just to describe concept in full). But the C2-like things (with friend=20
classes) often happen in my practice.

Idea is that C2 allows only certain combinations of field values. But=20
within the C2 itself there is not enough information to construct a new=20
(correct) combination (to check its correctness), that's why C2 itself=20
has no constructors. Instead it declares C2H as its friend and C2=20
instances are created within C2H. Due to prohibition of=20
private-aggregate construction I need to create a private identity=20
constructor (e.g. `private: C2(t1 v1, t2 v2, t3 v2): v1(v1), v2(v2),=20
v3(v3) {}`) inside every C2-like class to make it convenient to=20
construct it from within C2H; without that limitation I'd just use=20
aggregate construction for C2 within C2H (possibly deleting default=20
constructor of C2 with `C2() =3D delete;`, if needed).

For example, let C2 be iterator class and C2H be a container class.=20
Without information about owner-container, iterator is able to be only=20
default-constructed, but container constructs specific iterators:

 =C2=A0=C2=A0=C2=A0 class my_container {
 =C2=A0=C2=A0=C2=A0 public:
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 using value_type =3D =E2=80=A6;

 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 class iterator {
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 public:
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 friend =
my_container;
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 value_t=
ype &operator*() const;
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 value_t=
ype *operator->() const;
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=80=
=A6;
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 private:
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 t1 v1;
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 t2 v2;
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 };

 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 iterator begin() {return {.v1 =
=3D =E2=80=A6; .v2 =3D =E2=80=A6;};} //OOPS
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 iterator end() {return {.v1 =3D=
 =E2=80=A6; .v2 =3D =E2=80=A6;};} //OOPS
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 iterator find(=E2=80=A6) {=E2=
=80=A6; return {.v1 =3D =E2=80=A6; .v2 =3D =E2=80=A6;};} //OOPS
 =C2=A0=C2=A0=C2=A0 };

Every such case currently needs a boilerplate constructor in inner=20
class. It would be nicer and more intuitive if they won't.

> See, the word "aggregate" was not chosen arbitrarily. The concept of=20
> "aggregate" is exactly what the word means=20
> <http://www.dictionary.com/browse/aggregate>: a thing made entirely of=20
> other things. An aggregate currently is an assemblage of objects, each=20
> of which can have any particular value which that subobject can store.

"Aggregateness" is actually a point-of-view-dependent thing. Things that=20
look like really secure black boxes from distant locations (e.g.=20
unrelated classes) always appear to be just a field-set from the close=20
distance (e.g. from within the class itself, from its friend-classes, etc).

> If you have a user-provided constructor, then the type isn't a mere=20
> aggregation of things.

The fact is that I HAVEN'T any user-provided constructor, because=20
C2-like classes cannot reliable construct themselves. Their=20
friend-classes construct them. Currently to construct them from friend=20
classes, we need some boilerplate code (either in C2: `private: C2(t1=20
v1, t2 v2, t3 v2): v1(v1), v2(v2), v3(v3) {}` instead of `C2() =3D=20
delete/default`; or in C2H: `C2 i; i.v1 =3D =E2=80=A6; i.v2 =3D =E2=80=A6; =
i.v3 =3D =E2=80=A6;=20
do_something(i);` instead of `do_something({=E2=80=A6})`).

> And if you don't have a constructor for `C1`, why are its members=20
> private? What are you doing with classes with private members that=20
> have no constructors?

C1-like things are more rare than C2-like. Still they also could happen=20
in practice. C1 could be a class that due to significantly-varying=20
construction conditions looks much clearer with construction via static=20
functions than with construction via constructors.

For example, let it be file_stream-like class (though it's probably not=20
the best example, but I can't right now imagine a better):

 =C2=A0=C2=A0=C2=A0 class my_file_stream {
 =C2=A0=C2=A0=C2=A0 public:
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 my_file_stream() =3D delete;
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 static my_file_stream create_fi=
le(=E2=80=A6arguments=E2=80=A6);
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 static my_file_stream open_file=
(=E2=80=A6arguments=E2=80=A6);
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=80=A6;
 =C2=A0=C2=A0=C2=A0 };

Of course can be workarounded with something like:

 =C2=A0=C2=A0=C2=A0 class my_file_stream {
 =C2=A0=C2=A0=C2=A0 public:
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 struct file_creation_tag {=E2=
=80=A6};
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 struct file_opening_tag {=E2=80=
=A6};
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 my_file_stream(file_creation_ta=
g, =E2=80=A6arguments=E2=80=A6);
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 my_file_stream(file_opening_tag=
, =E2=80=A6arguments=E2=80=A6);
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=80=A6;
 =C2=A0=C2=A0=C2=A0 };

But=E2=80=A6 Ehh=E2=80=A6 It would be better to much better to allow both w=
ays.

Or C1 can prefer static-method-style construction due to other reasons, e.g=
:

 =C2=A0=C2=A0=C2=A0 class C1 {
 =C2=A0=C2=A0=C2=A0 public:
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C1() =3D delete;
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 static std::optional<C1> create=
_if_possible(=E2=80=A6);
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=80=A6;
 =C2=A0=C2=A0=C2=A0 };

--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/1c50b848-8ddd-0c8a-4ecd-9ed5f069952c%40gmail.com=
..

--------------87C8F21A4C1213DBD9276D21
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<html>
  <head>
    <meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf-8=
">
  </head>
  <body text=3D"#000000" bgcolor=3D"#FFFFFF">
    <p><br>
    </p>
    <br>
    <div class=3D"moz-cite-prefix">On 11.01.18 18:17, Nicol Bolas wrote:<br=
>
    </div>
    <blockquote type=3D"cite"
      cite=3D"mid:9e80c7be-e2be-4945-a3ff-93886dd2b336@isocpp.org">
      <div dir=3D"ltr"><br>
        <br>
        On Thursday, January 11, 2018 at 9:59:43 AM UTC-5, Oleksandr
        Pikozh wrote:
        <blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left:
          0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">Maybe I
          am missing something.
          <br>
          <br>
          But from purely intuitive point of view, if this works=E2=80=A6
          <br>
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 class C {
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 public:
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C1=
 i{1, 2}; ///&lt;--here
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 };
          <br>
          <br>
          =E2=80=A6then these should also work:
          <br>
          <br>
          * Example 1 "aggregate-initializing private members within the
          class <br>
          definition":
          <br>
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 class C1 {
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 private:
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 public:
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C1=
 i{1, 2}; ///&lt;--here
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 };
          <br>
          <br>
          * Example 2 "aggregate-initializing private members from a
          friend class":
          <br>
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 class C2 {
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 public:
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 friend C2H;
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 private:
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 };
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 class C2H {
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 public:
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C2=
 i{1, 2}; ///&lt;--here
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 }
          <br>
          <br>
          * Example 3 "aggregate-initializing protected members from a
          descendant <br>
          class":
          <br>
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 class C3 {
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 protected:
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 };
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 class C3D: public C3 {
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 public:
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C3=
 i{1, 2}; ///&lt;--here
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
          <br>
          =C2=A0=C2=A0=C2=A0=C2=A0 }
          <br>
          <br>
          I.e. it is reasonable that aggregate initialization cannot be
          performed <br>
          when it tries to affect unaccessible fields. And it is also
          reasonable <br>
          that aggregate initialization cannot be performed even when at
          least one <br>
          of the class fields is unaccessible. But why aggregate
          initialization is <br>
          blocked for classes whose all fields are accessible from the
          current <br>
          scope, I don't understand.<br>
        </blockquote>
        <div><br>
        </div>
        <div>There is no technical reason why it has to be this way
          (though the recent `is_aggregate` trait would be made
          non-useful). But it's still not a good idea.</div>
        <div><br>
        </div>
        <div>First, the ordering of members in different access classes
          is undefined. So you would only be able to expand this to
          classes where NSDMs are all of the same access classes.</div>
        <div><br>
        </div>
        <div>Second, this wouldn't work in real code.</div>
        <div><br>
        </div>
        <div>Consider `C3`. Why are its members private?</div>
        <div><br>
        </div>
        <div>If you wanted a type that could hold any two integers, but
          you couldn't change them once set, you would just declare them
          `const` and leave them public. But you made them mutable. And
          private.</div>
        <div><br>
        </div>
        <div>Therefore, we must assume that you want to control the
          values of these two integers. To do that, you need to have a
          user-provided constructor. And once you have that, the type
          isn't an aggregate anymore.</div>
        <div><br>
        </div>
        <div>See, the word "aggregate" was not chosen arbitrarily. The
          concept of "aggregate" is exactly <a
            href=3D"http://www.dictionary.com/browse/aggregate"
            moz-do-not-send=3D"true">what the word means</a>: a thing made
          entirely of other things. An aggregate currently is an
          assemblage of objects, each of which can have any particular
          value which that subobject can store.</div>
        <div><br>
        </div>
        <div>If you have a user-provided constructor, then the type
          isn't a mere aggregation of things. Its member subobjects
          cannot assume any value; you're controlling what values are
          legal for your object. Therefore, once you have a constructor,
          the type isn't an aggregate anymore.</div>
        <div><br>
        </div>
        <div>And if you don't have a constructor for `C1`, why are its
          members private? What are you doing with classes with private
          members that have no constructors?</div>
      </div>
    </blockquote>
    <br>
    Let me answer point-by-point.<br>
    <br>
    <blockquote type=3D"cite">First, the ordering of members in different
      access classes is undefined. So you would only be able to expand
      this to classes where NSDMs are all of the same access classes.</bloc=
kquote>
    <br>
    Yes, undefined ordering of members in different access levels is a
    problem. But it can be workarounded by less-strict restriction: if a
    class has members of different access levels, allow it to be
    initialized only with C++20 designated initializers (e.g. `A a{.x =3D
    1, .y =3D2}`), otherwise allow it to be initialized by both designated
    and non-designated (e.g. `A a{1, 2}`) initializers. No need to
    forbid aggregate initialization at all.<br>
    <br>
    <blockquote type=3D"cite">
      <div>Second, this wouldn't work in real code.</div>
      <div><br>
      </div>
      <div>Consider `C3`. Why are its members private?</div>
    </blockquote>
    <br>
    Example C3 with protected members was purely theoretical (I provided
    it just to describe concept in full). But the C2-like things (with
    friend classes) often happen in my practice.<br>
    <br>
    Idea is that C2 allows only certain combinations of field values.
    But within the C2 itself there is not enough information to
    construct a new (correct) combination (to check its correctness),
    that's why C2 itself has no constructors. Instead it declares C2H as
    its friend and C2 instances are created within C2H. Due to <span
      id=3D"result_box" class=3D"short_text" lang=3D"en"><span class=3D"">p=
rohibition
        of </span></span> private-aggregate construction I need to
    create a private identity constructor (e.g. `private: C2(t1 v1, t2
    v2, t3 v2): v1(v1), v2(v2), v3(v3) {}`) inside every C2-like class
    to make it convenient to construct it from within C2H; without that
    limitation I'd just use aggregate construction for C2 within C2H
    (possibly deleting default constructor of C2 with `C2() =3D delete;`,
    if needed).<br>
    <br>
    For example, let C2 be iterator class and C2H be a container class.
    Without information about owner-container, iterator is able to be
    only default-constructed, but container constructs specific
    iterators:<br>
    <br>
    =C2=A0=C2=A0=C2=A0 class my_container {<br>
    =C2=A0=C2=A0=C2=A0 public:<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 using value_type =3D =E2=80=
=A6;<br>
    <br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 class iterator {<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 public:<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 frie=
nd my_container;<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 valu=
e_type &amp;operator*() const;<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 valu=
e_type *operator-&gt;() const;<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=
=80=A6;<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 private:<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 t1 v=
1;<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 t2 v=
2;<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 };<br>
    <br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 iterator begin() {return {.v=
1 =3D =E2=80=A6; .v2 =3D =E2=80=A6;};} //OOPS<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 iterator end() {return {.v1 =
=3D =E2=80=A6; .v2 =3D =E2=80=A6;};} //OOPS<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 iterator find(=E2=80=A6) {=
=E2=80=A6; return {.v1 =3D =E2=80=A6; .v2 =3D =E2=80=A6;};} //OOPS<br>
    =C2=A0=C2=A0=C2=A0 };<br>
    <br>
    Every such case currently needs a boilerplate constructor in inner
    class. It would be nicer and more intuitive if they won't.<br>
    <br>
    <blockquote type=3D"cite">See, the word "aggregate" was not chosen
      arbitrarily. The concept of "aggregate" is exactly <a
        href=3D"http://www.dictionary.com/browse/aggregate">what the word
        means</a>: a thing made entirely of other things. An aggregate
      currently is an assemblage of objects, each of which can have any
      particular value which that subobject can store.</blockquote>
    <br>
    "Aggregateness" is actually a point-of-view-dependent thing. Things
    that look like really secure black boxes from distant locations
    (e.g. unrelated classes) always appear to be just a field-set from
    the close distance (e.g. from within the class itself, from its
    friend-classes, etc).<br>
    <br>
    <blockquote type=3D"cite">If you have a user-provided constructor,
      then the type isn't a mere aggregation of things.</blockquote>
    <br>
    The fact is that I HAVEN'T any user-provided constructor, because
    C2-like classes cannot reliable construct themselves. Their
    friend-classes construct them. Currently to construct them from
    friend classes, we need some boilerplate code (either in C2:
    `private: C2(t1 v1, t2 v2, t3 v2): v1(v1), v2(v2), v3(v3) {}`
    instead of `C2() =3D delete/default`; or in C2H: `C2 i; i.v1 =3D =E2=80=
=A6; i.v2
    =3D =E2=80=A6; i.v3 =3D =E2=80=A6; do_something(i);` instead of `do_som=
ething({=E2=80=A6})`).<br>
    <br>
    <blockquote type=3D"cite">And if you don't have a constructor for
      `C1`, why are its members private? What are you doing with classes
      with private members that have no constructors?</blockquote>
    <br>
    C1-like things are more rare than C2-like. Still they also could
    happen in practice. C1 could be a class that due to
    significantly-varying construction conditions looks much clearer
    with construction via static functions than with construction via
    constructors.<br>
    <br>
    For example, let it be file_stream-like class (though it's probably
    not the best example, but I can't right now imagine a better):<br>
    <br>
    =C2=A0=C2=A0=C2=A0 class my_file_stream {<br>
    =C2=A0=C2=A0=C2=A0 public:<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 my_file_stream() =3D delete;=
<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 static my_file_stream create=
_file(=E2=80=A6arguments=E2=80=A6);<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 static my_file_stream open_f=
ile(=E2=80=A6arguments=E2=80=A6);<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=80=A6;<br>
    =C2=A0=C2=A0=C2=A0 };<br>
    <br>
    Of course can be workarounded with something like:<br>
    <br>
    =C2=A0=C2=A0=C2=A0 class my_file_stream {<br>
    =C2=A0=C2=A0=C2=A0 public:<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 struct file_creation_tag {=
=E2=80=A6};<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 struct file_opening_tag {=E2=
=80=A6};<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 my_file_stream(file_creation=
_tag, =E2=80=A6arguments=E2=80=A6);<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 my_file_stream(file_opening_=
tag, =E2=80=A6arguments=E2=80=A6);<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=80=A6;<br>
    =C2=A0=C2=A0=C2=A0 };<br>
    <br>
    But=E2=80=A6 Ehh=E2=80=A6 It would be better to much better to allow bo=
th ways.<br>
    <br>
    Or C1 can prefer static-method-style construction due to other
    reasons, e.g:<br>
    <br>
    =C2=A0=C2=A0=C2=A0 class C1 {<br>
    =C2=A0=C2=A0=C2=A0 public:<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C1() =3D delete;<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 static std::optional&lt;C1&g=
t; create_if_possible(=E2=80=A6);<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=80=A6;<br>
    =C2=A0=C2=A0=C2=A0 };<br>
    <br>
  </body>
</html>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/1c50b848-8ddd-0c8a-4ecd-9ed5f069952c%=
40gmail.com?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/1c50b848-8ddd-0c8a-4ecd-9ed5f069952c%=
40gmail.com</a>.<br />

--------------87C8F21A4C1213DBD9276D21--

.


Author: Nicol Bolas <jmckesson@gmail.com>
Date: Thu, 11 Jan 2018 10:17:28 -0800 (PST)
Raw View
------=_Part_658_561211532.1515694648698
Content-Type: multipart/alternative;
 boundary="----=_Part_659_744532747.1515694648698"

------=_Part_659_744532747.1515694648698
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable



On Thursday, January 11, 2018 at 12:39:06 PM UTC-5, Oleksandr Pikozh wrote:
>
> On 11.01.18 18:17, Nicol Bolas wrote:
> Let me answer point-by-point.
>
> First, the ordering of members in different access classes is undefined.=
=20
> So you would only be able to expand this to classes where NSDMs are all o=
f=20
> the same access classes.
>
>
> Yes, undefined ordering of members in different access levels is a=20
> problem. But it can be workarounded by less-strict restriction: if a clas=
s=20
> has members of different access levels, allow it to be initialized only=
=20
> with C++20 designated initializers (e.g. `A a{.x =3D 1, .y =3D2}`), other=
wise=20
> allow it to be initialized by both designated and non-designated (e.g. `A=
=20
> a{1, 2}`) initializers. No need to forbid aggregate initialization at all=
..
>

Designated initializers aren't allowed to break ordering. So if there is no=
=20
ordering in the case of different access classes, then there can be no=20
ordering with designated initializers either.

> See, the word "aggregate" was not chosen arbitrarily. The concept of=20
> "aggregate" is exactly what the word means=20
> <http://www.dictionary.com/browse/aggregate>: a thing made entirely of=20
> other things. An aggregate currently is an assemblage of objects, each of=
=20
> which can have any particular value which that subobject can store.
>
>
> "Aggregateness" is actually a point-of-view-dependent thing. Things that=
=20
> look like really secure black boxes from distant locations (e.g. unrelate=
d=20
> classes) always appear to be just a field-set from the close distance (e.=
g.=20
> from within the class itself, from its friend-classes, etc).
>

I understand that this is what you *want* to be the case. But that's a very=
=20
fundamental change in the very nature of a C++ concept. And I don't believe=
=20
it improves the language in a useful way.

Thus far, the principle advantage of this viewpoint is that you get to=20
avoid having to write one or more constructors that forward their=20
parameters. That improves DRY to a degree. And it does mesh well with=20
default member initializers.

But it seems to me that what you really want is a way to create=20
constructors that take parameters in the order that members are declared=20
and forward them directly to the appropriate subobjects. Aggregate=20
initialization is merely a means for you to achieve that.

Also, by defining a way in the language to create such constructor sets,=20
you also get to neatly sidestep the ordering problem of members of=20
different access classes. Because by definition in C++, all subobjcts of a=
=20
class are constructed in the order they appear in the class definition.=20
Since that ordering already exists, we can say that the order of parameters=
=20
to such a generated constructor must be the order they appear in the class,=
=20
so that they can be properly forwarded in that order.

Don't ask about what such a syntax might look like though. The best=20
strawman I could come up with is `type_name() =3D auto;`.

I think a type being an aggregate has to mean something more than a type=20
which has constructors that just so happen to forward parameters to its=20
subobjects.

> If you have a user-provided constructor, then the type isn't a mere=20
> aggregation of things.
>
>
> The fact is that I HAVEN'T any user-provided constructor, because C2-like=
=20
> classes cannot reliable construct themselves. Their friend-classes=20
> construct them.
>

That distinction makes absolutely no sense. Types with constructors do not=
=20
"construct themselves"; they are *always* constructed by someone else=20
calling the constructor and providing parameters.
=20

> Currently to construct them from friend classes, we need some boilerplate=
=20
> code (either in C2: `private: C2(t1 v1, t2 v2, t3 v2): v1(v1), v2(v2),=20
> v3(v3) {}` instead of `C2() =3D delete/default`; or in C2H: `C2 i; i.v1 =
=3D =E2=80=A6;=20
> i.v2 =3D =E2=80=A6; i.v3 =3D =E2=80=A6; do_something(i);` instead of `do_=
something({=E2=80=A6})`).
>
> And if you don't have a constructor for `C1`, why are its members private=
?=20
> What are you doing with classes with private members that have no=20
> constructors?
>
>
> C1-like things are more rare than C2-like. Still they also could happen i=
n=20
> practice. C1 could be a class that due to significantly-varying=20
> construction conditions looks much clearer with construction via static=
=20
> functions than with construction via constructors.
>
> For example, let it be file_stream-like class (though it's probably not=
=20
> the best example, but I can't right now imagine a better):
>
>     class my_file_stream {
>     public:
>         my_file_stream() =3D delete;
>         static my_file_stream create_file(=E2=80=A6arguments=E2=80=A6);
>         static my_file_stream open_file(=E2=80=A6arguments=E2=80=A6);
>         =E2=80=A6;
>     };
>
> Of course can be workarounded with something like:
>
>     class my_file_stream {
>     public:
>         struct file_creation_tag {=E2=80=A6};
>         struct file_opening_tag {=E2=80=A6};
>         my_file_stream(file_creation_tag, =E2=80=A6arguments=E2=80=A6);
>         my_file_stream(file_opening_tag, =E2=80=A6arguments=E2=80=A6);
>         =E2=80=A6;
>     };
>
> But=E2=80=A6 Ehh=E2=80=A6 It would be better to much better to allow both=
 ways.
>

Um, no it wouldn't. Why? Because in the latter case, I can call indirect=20
initialization functions like `make_shared`, `emplace_back` or whatever.=20
Through the constructor tag, I can have all the control of the static=20
functions, but I still get to construct the object in place without having=
=20
to do a move or whatever.

You could have the static/global functions as a slightly more convenient=20
interface, but that's just redundancy that serves little purpose.

Or C1 can prefer static-method-style construction due to other reasons, e.g=
:
>
>     class C1 {
>     public:
>         C1() =3D delete;
>         static std::optional<C1> create_if_possible(=E2=80=A6);
>         =E2=80=A6;
>     };
>
>

--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/d939dc68-ce02-4b06-b26d-d6532f7961c6%40isocpp.or=
g.

------=_Part_659_744532747.1515694648698
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><br><br>On Thursday, January 11, 2018 at 12:39:06 PM UTC-5=
, Oleksandr Pikozh wrote:<blockquote class=3D"gmail_quote" style=3D"margin:=
 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
 =20
   =20
 =20
  <div bgcolor=3D"#FFFFFF" text=3D"#000000">
    <p>On 11.01.18 18:17, Nicol Bolas wrote:<br></p>
    Let me answer point-by-point.<br>
    <br>
    <blockquote type=3D"cite">First, the ordering of members in different
      access classes is undefined. So you would only be able to expand
      this to classes where NSDMs are all of the same access classes.</bloc=
kquote>
    <br>
    Yes, undefined ordering of members in different access levels is a
    problem. But it can be workarounded by less-strict restriction: if a
    class has members of different access levels, allow it to be
    initialized only with C++20 designated initializers (e.g. `A a{.x =3D
    1, .y =3D2}`), otherwise allow it to be initialized by both designated
    and non-designated (e.g. `A a{1, 2}`) initializers. No need to
    forbid aggregate initialization at all.<br></div></blockquote><div><br>=
</div><div>Designated initializers aren&#39;t allowed to break ordering. So=
 if there is no ordering in the case of different access classes, then ther=
e can be no ordering with designated initializers either.<br></div><blockqu=
ote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left=
: 1px #ccc solid;padding-left: 1ex;"><div bgcolor=3D"#FFFFFF" text=3D"#0000=
00">
    <blockquote type=3D"cite">See, the word &quot;aggregate&quot; was not c=
hosen
      arbitrarily. The concept of &quot;aggregate&quot; is exactly <a onmou=
sedown=3D"this.href=3D&#39;http://www.google.com/url?q\x3dhttp%3A%2F%2Fwww.=
dictionary.com%2Fbrowse%2Faggregate\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjC=
NEH3WKo392YfXyomamMxKS1j1m9_Q&#39;;return true;" onclick=3D"this.href=3D&#3=
9;http://www.google.com/url?q\x3dhttp%3A%2F%2Fwww.dictionary.com%2Fbrowse%2=
Faggregate\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNEH3WKo392YfXyomamMxKS1j1=
m9_Q&#39;;return true;" href=3D"http://www.dictionary.com/browse/aggregate"=
 target=3D"_blank" rel=3D"nofollow">what the word
        means</a>: a thing made entirely of other things. An aggregate
      currently is an assemblage of objects, each of which can have any
      particular value which that subobject can store.</blockquote>
    <br>
    &quot;Aggregateness&quot; is actually a point-of-view-dependent thing. =
Things
    that look like really secure black boxes from distant locations
    (e.g. unrelated classes) always appear to be just a field-set from
    the close distance (e.g. from within the class itself, from its
    friend-classes, etc).<br></div></blockquote><div><br></div><div>I under=
stand that this is what you <i>want</i> to be the case. But that&#39;s a ve=
ry fundamental change in the very nature of a C++ concept. And I don&#39;t =
believe it improves the language in a useful way.</div><div><br></div><div>=
Thus far, the principle advantage of this viewpoint is that you get to avoi=
d having to write one or more constructors that forward their parameters. T=
hat improves DRY to a degree. And it does mesh well with default member ini=
tializers.</div><div><br></div><div>But it seems to me that what you really=
 want is a way to create constructors that take parameters in the order tha=
t members are declared and forward them directly to the appropriate subobje=
cts. Aggregate initialization is merely a means for you to achieve that.</d=
iv><div><br></div><div>Also, by defining a way in the language to create su=
ch constructor sets, you also get to neatly sidestep the ordering problem o=
f members of different access classes. Because by definition in C++, all su=
bobjcts of a class are constructed in the order they appear in the class de=
finition. Since that ordering already exists, we can say that the order of =
parameters to such a generated constructor must be the order they appear in=
 the class, so that they can be properly forwarded in that order.</div><div=
><br></div><div>Don&#39;t ask about what such a syntax might look like thou=
gh. The best strawman I could come up with is `type_name() =3D auto;`.</div=
><div><br></div><div>I think a type being an aggregate has to mean somethin=
g more than a type which has constructors that just so happen to forward pa=
rameters to its subobjects.<br></div><blockquote class=3D"gmail_quote" styl=
e=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left:=
 1ex;"><div bgcolor=3D"#FFFFFF" text=3D"#000000">
    <blockquote type=3D"cite">If you have a user-provided constructor,
      then the type isn&#39;t a mere aggregation of things.</blockquote>
    <br>
    The fact is that I HAVEN&#39;T any user-provided constructor, because
    C2-like classes cannot reliable construct themselves. Their
    friend-classes construct them.</div></blockquote><div><br></div><div>Th=
at distinction makes absolutely no sense. Types with constructors do not &q=
uot;construct themselves&quot;; they are <i>always</i> constructed by someo=
ne else calling the constructor and providing parameters.</div><div>=C2=A0<=
/div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8e=
x;border-left: 1px #ccc solid;padding-left: 1ex;"><div bgcolor=3D"#FFFFFF" =
text=3D"#000000">Currently to construct them from
    friend classes, we need some boilerplate code (either in C2:
    `private: C2(t1 v1, t2 v2, t3 v2): v1(v1), v2(v2), v3(v3) {}`
    instead of `C2() =3D delete/default`; or in C2H: `C2 i; i.v1 =3D =E2=80=
=A6; i.v2
    =3D =E2=80=A6; i.v3 =3D =E2=80=A6; do_something(i);` instead of `do_som=
ething({=E2=80=A6})`).<br>
    <br>
    <blockquote type=3D"cite">And if you don&#39;t have a constructor for
      `C1`, why are its members private? What are you doing with classes
      with private members that have no constructors?</blockquote>
    <br>
    C1-like things are more rare than C2-like. Still they also could
    happen in practice. C1 could be a class that due to
    significantly-varying construction conditions looks much clearer
    with construction via static functions than with construction via
    constructors.<br>
    <br>
    For example, let it be file_stream-like class (though it&#39;s probably
    not the best example, but I can&#39;t right now imagine a better):<br>
    <br>
    =C2=A0=C2=A0=C2=A0 class my_file_stream {<br>
    =C2=A0=C2=A0=C2=A0 public:<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 my_file_stream() =3D delete;=
<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 static my_file_stream create=
_file(=E2=80=A6arguments=E2=80=A6);<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 static my_file_stream open_f=
ile(=E2=80=A6arguments=E2=80=A6);<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=80=A6;<br>
    =C2=A0=C2=A0=C2=A0 };<br>
    <br>
    Of course can be workarounded with something like:<br>
    <br>
    =C2=A0=C2=A0=C2=A0 class my_file_stream {<br>
    =C2=A0=C2=A0=C2=A0 public:<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 struct file_creation_tag {=
=E2=80=A6};<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 struct file_opening_tag {=E2=
=80=A6};<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 my_file_stream(file_creation=
_<wbr>tag, =E2=80=A6arguments=E2=80=A6);<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 my_file_stream(file_opening_=
<wbr>tag, =E2=80=A6arguments=E2=80=A6);<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=80=A6;<br>
    =C2=A0=C2=A0=C2=A0 };<br>
    <br>
    But=E2=80=A6 Ehh=E2=80=A6 It would be better to much better to allow bo=
th ways.<br></div></blockquote><div><br></div><div>Um, no it wouldn&#39;t. =
Why? Because in the latter case, I can call indirect initialization functio=
ns like `make_shared`, `emplace_back` or whatever. Through the constructor =
tag, I can have all the control of the static functions, but I still get to=
 construct the object in place without having to do a move or whatever.</di=
v><div><br></div><div>You could have the static/global functions as a sligh=
tly more convenient interface, but that&#39;s just redundancy that serves l=
ittle purpose.</div><div><br></div><blockquote class=3D"gmail_quote" style=
=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: =
1ex;"><div bgcolor=3D"#FFFFFF" text=3D"#000000">Or C1 can prefer static-met=
hod-style construction due to other
    reasons, e.g:<br>
    <br>
    =C2=A0=C2=A0=C2=A0 class C1 {<br>
    =C2=A0=C2=A0=C2=A0 public:<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C1() =3D delete;<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 static std::optional&lt;C1&g=
t; create_if_possible(=E2=80=A6);<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=80=A6;<br>
    =C2=A0=C2=A0=C2=A0 };<br>
    <br>
  </div>

</blockquote></div>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/d939dc68-ce02-4b06-b26d-d6532f7961c6%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/d939dc68-ce02-4b06-b26d-d6532f7961c6=
%40isocpp.org</a>.<br />

------=_Part_659_744532747.1515694648698--

------=_Part_658_561211532.1515694648698--

.


Author: Oleksandr Pikozh <o.pikozh@gmail.com>
Date: Thu, 11 Jan 2018 20:51:18 +0200
Raw View
This is a multi-part message in MIME format.
--------------A18538F642561D9590A1FCC5
Content-Type: text/plain; charset="UTF-8"; format=flowed
Content-Transfer-Encoding: quoted-printable

On 11.01.18 20:17, Nicol Bolas wrote:
> Designated initializers aren't allowed to break ordering. So if there=20
> is no ordering in the case of different access classes, then there can=20
> be no ordering with designated initializers either.
OK, I get it.

> But that's a very fundamental change in the very nature of a C++ concept.
OK.

> But it seems to me that what you really want is a way to create=20
> constructors that take parameters in the order that members are=20
> declared and forward them directly to the appropriate subobjects.=20
> Aggregate initialization is merely a means for you to achieve that.
>
> Also, by defining a way in the language to create such constructor=20
> sets, you also get to neatly sidestep the ordering problem of members=20
> of different access classes. Because by definition in C++, all=20
> subobjcts of a class are constructed in the order they appear in the=20
> class definition. Since that ordering already exists, we can say that=20
> the order of parameters to such a generated constructor must be the=20
> order they appear in the class, so that they can be properly forwarded=20
> in that order.
>
> Don't ask about what such a syntax might look like though. The best=20
> strawman I could come up with is `type_name() =3D auto;`.
Personally I would really want a way to explicitly enable/disable=20
aggregate-constructor syntax.
E.g. `type_name{} =3D default;` (with meaning "yeah, maybe I declared a=20
custom constructor for this, but I still want the aggregate-constructor=20
syntax for it (either publicly, or privately only)") and `type_name{} =3D=
=20
delete;`; and not to have it auto-disabled in unexpected cases.
However I understand that it may appear incompatible with existing C++=20
model.

> Types with constructors do not "construct themselves"; they are=20
> /always/ constructed by someone else calling the constructor and=20
> providing parameters.
Theoretically yes, class instances are always constructed by some outer=20
context.
But as in practice I strongly prefer=E2=80=A6
 =C2=A0=C2=A0=C2=A0 class vector {
 =C2=A0=C2=A0=C2=A0 public:
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 class iterator {=E2=80=A6};
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 iterator begin();
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 iterator end();
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=80=A6;
 =C2=A0=C2=A0=C2=A0 };
=E2=80=A6over=E2=80=A6
 =C2=A0=C2=A0=C2=A0 class vector {
 =C2=A0=C2=A0=C2=A0 public:
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 class iterator {
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 public:
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 struct =
begin_tag {};
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 struct =
end_tag {};
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 iterato=
r(vector &, begin_tag);
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 iterato=
r(vector &, end_tag);
 =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0 =C2=A0=C2=A0 =C2=A0 =E2=80=A6;
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 };
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=80=A6;
 =C2=A0=C2=A0=C2=A0 };
=E2=80=A6therefore I need somehow to differentiate classes that are constru=
cted=20
from outside primarily by constructors from classes that are constructed=20
from outside primarily by methods, so I used a quite-imprecise phrase=20
"construct themselves".

> Um, no it wouldn't. Why? Because in the latter case, I can call=20
> indirect initialization functions like `make_shared`, `emplace_back`=20
> or whatever. Through the constructor tag, I can have all the control=20
> of the static functions, but I still get to construct the object in=20
> place without having to do a move or whatever.
IMHO, that distinction between constructors and=20
nonvoid-returning-functions is one of greatest faults in C++ design=20
(however I understand that it's probably too late now to break it (the=20
distinction) in a compatible way).

OK, thanks for your attention.

--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/cba3f7ac-22b1-8dde-c406-7be4c5d67055%40gmail.com=
..

--------------A18538F642561D9590A1FCC5
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<html>
  <head>
    <meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf-8=
">
  </head>
  <body text=3D"#000000" bgcolor=3D"#FFFFFF">
    On 11.01.18 20:17, Nicol Bolas wrote:<br>
    <blockquote type=3D"cite"
      cite=3D"mid:d939dc68-ce02-4b06-b26d-d6532f7961c6@isocpp.org">
      <div dir=3D"ltr">Designated initializers aren't allowed to break
        ordering. So if there is no ordering in the case of different
        access classes, then there can be no ordering with designated
        initializers either.<br>
      </div>
    </blockquote>
    OK, I get it.<br>
    <br>
    <blockquote type=3D"cite">But that's a very fundamental change in the
      very nature of a C++ concept.</blockquote>
    OK.<br>
    <br>
    <blockquote type=3D"cite"
      cite=3D"mid:d939dc68-ce02-4b06-b26d-d6532f7961c6@isocpp.org">
      <div dir=3D"ltr">But it seems to me that what you really want is a
        way to create constructors that take parameters in the order
        that members are declared and forward them directly to the
        appropriate subobjects. Aggregate initialization is merely a
        means for you to achieve that.
        <div><br>
        </div>
        <div>Also, by defining a way in the language to create such
          constructor sets, you also get to neatly sidestep the ordering
          problem of members of different access classes. Because by
          definition in C++, all subobjcts of a class are constructed in
          the order they appear in the class definition. Since that
          ordering already exists, we can say that the order of
          parameters to such a generated constructor must be the order
          they appear in the class, so that they can be properly
          forwarded in that order.</div>
        <div><br>
        </div>
        <div>Don't ask about what such a syntax might look like though.
          The best strawman I could come up with is `type_name() =3D
          auto;`.</div>
      </div>
    </blockquote>
    Personally I would really want a way to explicitly enable/disable
    aggregate-constructor syntax.<br>
    E.g. `type_name{} =3D default;` (with meaning "yeah, maybe I declared
    a custom constructor for this, but I still want the
    aggregate-constructor syntax for it (either publicly, or privately
    only)") and `type_name{} =3D delete;`; and not to have it
    auto-disabled in unexpected cases.<br>
    However I understand that it may appear incompatible with existing
    C++ model.<br>
    <br>
    <blockquote type=3D"cite"
      cite=3D"mid:d939dc68-ce02-4b06-b26d-d6532f7961c6@isocpp.org">
      <div dir=3D"ltr">Types with constructors do not "construct
        themselves"; they are <i>always</i> constructed by someone else
        calling the constructor and providing parameters.</div>
    </blockquote>
    Theoretically yes, class instances are always constructed by some
    outer context.<br>
    But as in practice I strongly prefer=E2=80=A6<br>
    =C2=A0=C2=A0=C2=A0 class vector {<br>
    =C2=A0=C2=A0=C2=A0 public:<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 class iterator {=E2=80=A6};<=
br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 iterator begin();<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 iterator end();<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=80=A6;<br>
    =C2=A0=C2=A0=C2=A0 };<br>
    =E2=80=A6over=E2=80=A6<br>
    =C2=A0=C2=A0=C2=A0 class vector {<br>
    =C2=A0=C2=A0=C2=A0 public:<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 class iterator {<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 public:<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 stru=
ct begin_tag {};<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 stru=
ct end_tag {};<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 iter=
ator(vector &amp;, begin_tag);<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 iter=
ator(vector &amp;, end_tag);<br>
    =C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0 =C2=A0=C2=A0 =C2=A0 =E2=80=A6;<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 };<br>
    =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =E2=80=A6;<br>
    =C2=A0=C2=A0=C2=A0 };<br>
    =E2=80=A6therefore I need somehow to differentiate classes that are
    constructed from outside primarily by constructors from classes that
    are constructed from outside primarily by methods, so I used a
    quite-imprecise phrase "construct themselves".<br>
    <br>
    <blockquote type=3D"cite"
      cite=3D"mid:d939dc68-ce02-4b06-b26d-d6532f7961c6@isocpp.org">
      <div dir=3D"ltr">Um, no it wouldn't. Why? Because in the latter
        case, I can call indirect initialization functions like
        `make_shared`, `emplace_back` or whatever. Through the
        constructor tag, I can have all the control of the static
        functions, but I still get to construct the object in place
        without having to do a move or whatever.</div>
    </blockquote>
    IMHO, that distinction between constructors and
    nonvoid-returning-functions is one of greatest faults in C++ design
    (however I understand that it's probably too late now to break it
    (the distinction) in a compatible way).<br>
    <br>
    OK, thanks for your attention.<br>
  </body>
</html>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/cba3f7ac-22b1-8dde-c406-7be4c5d67055%=
40gmail.com?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/cba3f7ac-22b1-8dde-c406-7be4c5d67055%=
40gmail.com</a>.<br />

--------------A18538F642561D9590A1FCC5--

.


Author: Edward Catmur <ed@catmur.co.uk>
Date: Thu, 11 Jan 2018 16:20:48 -0800 (PST)
Raw View
------=_Part_732_1303810768.1515716448332
Content-Type: multipart/alternative;
 boundary="----=_Part_733_378975435.1515716448332"

------=_Part_733_378975435.1515716448332
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable



On Thursday, 11 January 2018 16:17:48 UTC, Nicol Bolas wrote:
>
>
>
> On Thursday, January 11, 2018 at 9:59:43 AM UTC-5, Oleksandr Pikozh wrote=
:
>>
>> Maybe I am missing something.=20
>>
>> But from purely intuitive point of view, if this works=E2=80=A6=20
>>
>>      class C {=20
>>      public:=20
>>          int f1, f2;=20
>>          static void s() {=20
>>              C1 i{1, 2}; ///<--here=20
>>          }=20
>>      };=20
>>
>> =E2=80=A6then these should also work:=20
>>
>> * Example 1 "aggregate-initializing private members within the class=20
>> definition":=20
>>
>>      class C1 {=20
>>      private:=20
>>          int f1, f2;=20
>>      public:=20
>>          static void s() {=20
>>              C1 i{1, 2}; ///<--here=20
>>          }=20
>>      };=20
>>
>> * Example 2 "aggregate-initializing private members from a friend class"=
:=20
>>
>>      class C2 {=20
>>      public:=20
>>          friend C2H;=20
>>      private:=20
>>          int f1, f2;=20
>>      };=20
>>      class C2H {=20
>>      public:=20
>>          static void s() {=20
>>              C2 i{1, 2}; ///<--here=20
>>          }=20
>>      }=20
>>
>> * Example 3 "aggregate-initializing protected members from a descendant=
=20
>> class":=20
>>
>>      class C3 {=20
>>      protected:=20
>>          int f1, f2;=20
>>      };=20
>>      class C3D: public C3 {=20
>>      public:=20
>>          static void s() {=20
>>              C3 i{1, 2}; ///<--here=20
>>          }=20
>>      }=20
>>
>> I.e. it is reasonable that aggregate initialization cannot be performed=
=20
>> when it tries to affect unaccessible fields. And it is also reasonable=
=20
>> that aggregate initialization cannot be performed even when at least one=
=20
>> of the class fields is unaccessible. But why aggregate initialization is=
=20
>> blocked for classes whose all fields are accessible from the current=20
>> scope, I don't understand.
>>
>
> There is no technical reason why it has to be this way (though the recent=
=20
> `is_aggregate` trait would be made non-useful). But it's still not a good=
=20
> idea.
>
> First, the ordering of members in different access classes is undefined.=
=20
> So you would only be able to expand this to classes where NSDMs are all o=
f=20
> the same access classes.
>

I don't see that the layout in-memory of a mixed-access class is relevant.=
=20
The logical ordering of members as observed by the sequence in which NSDMs=
=20
are constructed and (in reverse) destructed is still the same as the=20
overall lexical order of members within the class definition. Anyone who=20
has the poor taste to write a class with mixed-access NSDMs is unlikely to=
=20
expect the aggregate construction order to follow in-memory order over=20
lexical and runtime sequential order.
=20

> Second, this wouldn't work in real code.
>
> Consider `C3`. Why are its members private?
>
> If you wanted a type that could hold any two integers, but you couldn't=
=20
> change them once set, you would just declare them `const` and leave them=
=20
> public. But you made them mutable. And private.
>
> Therefore, we must assume that you want to control the values of these tw=
o=20
> integers. To do that, you need to have a user-provided constructor. And=
=20
> once you have that, the type isn't an aggregate anymore.
>
> See, the word "aggregate" was not chosen arbitrarily. The concept of=20
> "aggregate" is exactly what the word means=20
> <http://www.dictionary.com/browse/aggregate>: a thing made entirely of=20
> other things. An aggregate currently is an assemblage of objects, each of=
=20
> which can have any particular value which that subobject can store.
>
> If you have a user-provided constructor, then the type isn't a mere=20
> aggregation of things. Its member subobjects cannot assume any value;=20
> you're controlling what values are legal for your object. Therefore, once=
=20
> you have a constructor, the type isn't an aggregate anymore.
>
> And if you don't have a constructor for `C1`, why are its members private=
?=20
> What are you doing with classes with private members that have no=20
> constructors?
>

--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/4160b2a5-4c71-4a49-9c04-c39df6782fa3%40isocpp.or=
g.

------=_Part_733_378975435.1515716448332
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><br><br>On Thursday, 11 January 2018 16:17:48 UTC, Nicol B=
olas  wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-lef=
t: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><=
br><br>On Thursday, January 11, 2018 at 9:59:43 AM UTC-5, Oleksandr Pikozh =
wrote:<blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex=
;border-left:1px #ccc solid;padding-left:1ex">Maybe I am missing something.
<br>
<br>But from purely intuitive point of view, if this works=E2=80=A6
<br>
<br>=C2=A0=C2=A0=C2=A0=C2=A0 class C {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 public:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C1 i{1, =
2}; ///&lt;--here
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
<br>=C2=A0=C2=A0=C2=A0=C2=A0 };
<br>
<br>=E2=80=A6then these should also work:
<br>
<br>* Example 1 &quot;aggregate-initializing private members within the cla=
ss=20
<br>definition&quot;:
<br>
<br>=C2=A0=C2=A0=C2=A0=C2=A0 class C1 {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 private:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
<br>=C2=A0=C2=A0=C2=A0=C2=A0 public:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C1 i{1, =
2}; ///&lt;--here
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
<br>=C2=A0=C2=A0=C2=A0=C2=A0 };
<br>
<br>* Example 2 &quot;aggregate-initializing private members from a friend =
class&quot;:
<br>
<br>=C2=A0=C2=A0=C2=A0=C2=A0 class C2 {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 public:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 friend C2H;
<br>=C2=A0=C2=A0=C2=A0=C2=A0 private:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
<br>=C2=A0=C2=A0=C2=A0=C2=A0 };
<br>=C2=A0=C2=A0=C2=A0=C2=A0 class C2H {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 public:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C2 i{1, =
2}; ///&lt;--here
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
<br>=C2=A0=C2=A0=C2=A0=C2=A0 }
<br>
<br>* Example 3 &quot;aggregate-initializing protected members from a desce=
ndant=20
<br>class&quot;:
<br>
<br>=C2=A0=C2=A0=C2=A0=C2=A0 class C3 {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 protected:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 int f1, f2;
<br>=C2=A0=C2=A0=C2=A0=C2=A0 };
<br>=C2=A0=C2=A0=C2=A0=C2=A0 class C3D: public C3 {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 public:
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 static void s() {
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 C3 i{1, =
2}; ///&lt;--here
<br>=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 }
<br>=C2=A0=C2=A0=C2=A0=C2=A0 }
<br>
<br>I.e. it is reasonable that aggregate initialization cannot be performed=
=20
<br>when it tries to affect unaccessible fields. And it is also reasonable=
=20
<br>that aggregate initialization cannot be performed even when at least on=
e=20
<br>of the class fields is unaccessible. But why aggregate initialization i=
s=20
<br>blocked for classes whose all fields are accessible from the current=20
<br>scope, I don&#39;t understand.<br></blockquote><div><br></div><div>Ther=
e is no technical reason why it has to be this way (though the recent `is_a=
ggregate` trait would be made non-useful). But it&#39;s still not a good id=
ea.</div><div><br></div><div>First, the ordering of members in different ac=
cess classes is undefined. So you would only be able to expand this to clas=
ses where NSDMs are all of the same access classes.</div></div></blockquote=
><div><br></div><div>I don&#39;t see that the layout in-memory of a mixed-a=
ccess class is relevant. The logical ordering of members as observed by the=
 sequence in which NSDMs are constructed and (in reverse) destructed is sti=
ll the same as the overall lexical order of members within the class defini=
tion. Anyone who has the poor taste to write a class with mixed-access NSDM=
s is unlikely to expect the aggregate construction order to follow in-memor=
y order over lexical and runtime sequential order.</div><div>=C2=A0</div><b=
lockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;borde=
r-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div></div><div=
>Second, this wouldn&#39;t work in real code.</div><div><br></div><div>Cons=
ider `C3`. Why are its members private?</div><div><br></div><div>If you wan=
ted a type that could hold any two integers, but you couldn&#39;t change th=
em once set, you would just declare them `const` and leave them public. But=
 you made them mutable. And private.</div><div><br></div><div>Therefore, we=
 must assume that you want to control the values of these two integers. To =
do that, you need to have a user-provided constructor. And once you have th=
at, the type isn&#39;t an aggregate anymore.</div><div><br></div><div>See, =
the word &quot;aggregate&quot; was not chosen arbitrarily. The concept of &=
quot;aggregate&quot; is exactly <a href=3D"http://www.dictionary.com/browse=
/aggregate" target=3D"_blank" rel=3D"nofollow" onmousedown=3D"this.href=3D&=
#39;http://www.google.com/url?q\x3dhttp%3A%2F%2Fwww.dictionary.com%2Fbrowse=
%2Faggregate\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNEH3WKo392YfXyomamMxKS1=
j1m9_Q&#39;;return true;" onclick=3D"this.href=3D&#39;http://www.google.com=
/url?q\x3dhttp%3A%2F%2Fwww.dictionary.com%2Fbrowse%2Faggregate\x26sa\x3dD\x=
26sntz\x3d1\x26usg\x3dAFQjCNEH3WKo392YfXyomamMxKS1j1m9_Q&#39;;return true;"=
>what the word means</a>: a thing made entirely of other things. An aggrega=
te currently is an assemblage of objects, each of which can have any partic=
ular value which that subobject can store.</div><div><br></div><div>If you =
have a user-provided constructor, then the type isn&#39;t a mere aggregatio=
n of things. Its member subobjects cannot assume any value; you&#39;re cont=
rolling what values are legal for your object. Therefore, once you have a c=
onstructor, the type isn&#39;t an aggregate anymore.</div><div><br></div><d=
iv>And if you don&#39;t have a constructor for `C1`, why are its members pr=
ivate? What are you doing with classes with private members that have no co=
nstructors?</div></div></blockquote></div>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/4160b2a5-4c71-4a49-9c04-c39df6782fa3%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/4160b2a5-4c71-4a49-9c04-c39df6782fa3=
%40isocpp.org</a>.<br />

------=_Part_733_378975435.1515716448332--

------=_Part_732_1303810768.1515716448332--

.