Topic: Inline Classes (was "Properties" support in C++)
Author: =?UTF-8?Q?Klaim_=2D_Jo=C3=ABl_Lamotte?= <mjklaim@gmail.com>
Date: Sun, 30 Aug 2015 21:13:08 +0200
Raw View
--001a11c3170a73c1d9051e8c1b91
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On 30 August 2015 at 18:49, Nicol Bolas <jmckesson@gmail.com> wrote:
> On Sunday, August 30, 2015 at 10:05:49 AM UTC-4, Klaim - Jo=C3=ABl Lamott=
e
> wrote:
>>
>> I think your inline class idea better have it's own discussion thread so
>> that exploring the concept can be focused on it.
>>
>
> The thing it most needs is justification is why we need such a feature.
> Can it do something more than give us property hack fixes?
>
>
My suggestion was to discuss this outside the properties thread because
it's impossible to follow with the several sub-discussions ongoing.
Also, the "justification", however weak, have already been given in the
discussion.
> A more useful thing to explore is the concept of empty member
> optimizations (which would be necessary to make properties worthwhile wit=
h
> these). It wouldn't be acceptable to have the standard suddenly break lot=
s
> of code by declaring that all empty classes that are members of other
> classes no longer take up space.
>
> But would it be possible to decorate a class (using contextual keywords
> ala `final`) such that the class would be considered to be truly empty. A=
nd
> that such truly empty classes would not take up space in structs. If that
> is possible, it could have a multitude of uses.
>
I fail to see the relationship with the initial subject or with the
proposed inline class.
>
>
>> Another point: it seems to me that the definition of such construct coul=
d
>> be reused inside the same class or inside several classes.
>> So finding a way to avoid having to redefine it all the time would be
>> more interesting, I think.
>>
>
> That doesn't make sense, at least not for building properties out of them=
..
> With properties, you need to access *variables* from the owning class.
> And there's no way to do that with template metaprogramming. So each
> property-via-inline-class would have to be unique, coded for a specific
> variable or variables.
>
It's a feature proposal. Anything that don't already exists can be proposed
(and be rejected or not).
Like having a way to reuse this kind of code.
I'm not suggesting a specific solution, just pointing to a side effect of
the feature
that needs a solution.
On 30 August 2015 at 13:51, Miro Knejp <miro.knejp@gmail.com> wrote:
> Nicol, I think you misunderstand the concept of inline classes as
> discussed here, thought wasn't very well explained to be honest.
>
> Inline classes (like nested classes in Java) can only exist as part of
> their parent class and can access all their members and methods. Because
> they can only exist as part of their containing class there is no need fo=
r
> a this pointer, or storage if they have no members. The compiler can
> convert all access to the inline class to access to the parent object.
> Maybe translated to C++ the concept is more like a class-local namespace,
> but not quite. It cannot be copied out of a class because it can only exi=
st
> as part of its containing object.
>
> Whether calling it "class" or something else, is really just bikeshedding=
..
> One should first determine its conceptual and technical properties before
> starting the naming debate. I don't claim to have all the answers when it
> comes to applying this concept to C++ and I don't claim there aren't C++
> specificy problems that need solving. It just felt like the concept wasn'=
t
> explained very much and therfore caused ambiguity.
>
> Here some hypothetical example code:
>
> class A {
> int x_data;
> public:
> inline struct x {
> x& operator=3D(int value) {
> x_data =3D value; // Can access members of containing class
> return *this; // "this" can be impl converted A* <-> A::x*
> }
> int operator int() const { return x_data; }
> };
> };
> A a;
> a.x =3D 5; // Converted to a.x_operator=3D(5)
> cout << a.x; // Converted to a.x_operator_int()
>
> Even though the setter returns A::x&, the inline class A::x can only ever
> exist as reference/pointer outside of A and is only copyable as part of i=
ts
> containing object, therefore it is not a proxy. To the compiler A* and
> A::x* are the same value, they only change what operations are allowed on
> the target. In this example A::x has no data members therefore it
> conceptually doesn't require storage and &a.x =3D=3D &b.x is only ever tr=
ue if
> &a =3D=3D &b.
>
> A different example where the inline class does have members:
>
> class B {
> public:
> inline struct x {
> x& operator=3D(int value) { x_data =3D value; return *this; }
> int operator int() const { return x_data; }
> private:
> int data;
> };
> int foo() {
> return x.data * 2;
> }
> };
> B b;
> b.x =3D 5;
> cout << b.foo();
>
> Besides a way to implement properties inline classes can serve to group
> membes/methods the same way a non-inline nested class currently does exce=
pt
> it drops the requirement for a this member pointing to the parent. Even i=
f
> there was a dedicated property keyword/whatever the implementation
> technique could be inline classes and thefore kept open for advanced user=
s
> that want more than just the default getter/setter implementation.
>
> I hope this adequately explains what is meant by inline classes. It just
> seemed like it wasn't described very well and people were talking past ea=
ch
> other.
>
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
--001a11c3170a73c1d9051e8c1b91
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><br><div class=3D"gmail_extra"><br><div class=3D"gmail_quo=
te">On 30 August 2015 at 18:49, Nicol Bolas <span dir=3D"ltr"><<a href=
=3D"mailto:jmckesson@gmail.com" target=3D"_blank">jmckesson@gmail.com</a>&g=
t;</span> wrote:<br><blockquote class=3D"gmail_quote" style=3D"margin:0px 0=
px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);borde=
r-left-style:solid;padding-left:1ex"><div dir=3D"ltr"><span class=3D"">On S=
unday, August 30, 2015 at 10:05:49 AM UTC-4, Klaim - Jo=C3=ABl Lamotte wrot=
e:<blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;borde=
r-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid=
;padding-left:1ex"><div dir=3D"ltr">I think your inline class idea better h=
ave it's own discussion thread so that exploring the concept can be foc=
used on it.</div></blockquote></span><div><br>The thing it most needs is ju=
stification is why we need such a feature. Can it do something more than gi=
ve us property hack fixes?<br><br></div></div></blockquote><div><br></div><=
div>My suggestion was to discuss this outside the properties thread because=
it's impossible to follow with the several sub-discussions ongoing.</d=
iv><div><br></div><div>Also, the "justification", however weak, h=
ave already been given in the discussion.</div><div>=C2=A0</div><blockquote=
class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:=
1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left=
:1ex"><div dir=3D"ltr"><div>A more useful thing to explore is the concept o=
f empty member optimizations (which would be necessary to make properties w=
orthwhile with these). It wouldn't be acceptable to have the standard s=
uddenly break lots of code by declaring that all empty classes that are mem=
bers of other classes no longer take up space.<br><br></div></div></blockqu=
ote><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;bor=
der-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:sol=
id;padding-left:1ex"><div dir=3D"ltr"><div>But would it be possible to deco=
rate a class (using contextual keywords ala `final`) such that the class wo=
uld be considered to be truly empty. And that such truly empty classes woul=
d not take up space in structs. If that is possible, it could have a multit=
ude of uses.<br></div></div></blockquote><div><br></div><div><br></div><div=
><div>I fail to see the relationship with the initial subject or with the p=
roposed inline class.</div></div><div>=C2=A0</div><blockquote class=3D"gmai=
l_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-lef=
t-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir=
=3D"ltr"><div>=C2=A0</div><span class=3D""><blockquote class=3D"gmail_quote=
" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color=
:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr=
"><div>Another point: it seems to me that the definition of such construct =
could be reused inside the same class or inside several classes.</div><div>=
So finding a way to avoid having to redefine it all the time would be more =
interesting, I think.</div></div></blockquote></span><div><br>That doesn=
9;t make sense, at least not for building properties out of them. With prop=
erties, you need to access <i>variables</i> from the owning class. And ther=
e's no way to do that with template metaprogramming. So each property-v=
ia-inline-class would have to be unique, coded for a specific variable or v=
ariables.</div></div></blockquote><div><br></div><div>It's a feature pr=
oposal. Anything that don't already exists can be proposed (and be reje=
cted or not).=C2=A0</div><div>Like having a way to reuse this kind of code.=
</div><div>I'm not suggesting a specific solution, just pointing to a s=
ide effect of the feature</div><div>that needs a solution.</div><div><br></=
div><div>=C2=A0</div>On 30 August 2015 at 13:51, Miro Knejp=C2=A0<span dir=
=3D"ltr"><<a href=3D"mailto:miro.knejp@gmail.com" target=3D"_blank">miro=
..knejp@gmail.com</a>></span>=C2=A0wrote:<br><blockquote class=3D"gmail_q=
uote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-c=
olor:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><span style=
=3D"color:rgb(80,0,80);font-size:12.8000001907349px">Nicol, I think you mis=
understand the concept of inline classes as discussed here, thought wasn=
9;t very well explained to be honest.</span><br style=3D"color:rgb(80,0,80)=
;font-size:12.8000001907349px"><br style=3D"color:rgb(80,0,80);font-size:12=
..8000001907349px"><span style=3D"color:rgb(80,0,80);font-size:12.8000001907=
349px">Inline classes (like nested classes in Java) can only exist as part =
of their parent class and can access all their members and methods. Because=
they can only exist as part of their containing class there is no need for=
a this pointer, or storage if they have no members. The compiler can conve=
rt all access to the inline class to access to the parent object. Maybe tra=
nslated to C++ the concept is more like a class-local namespace, but not qu=
ite. It cannot be copied out of a class because it can only exist as part o=
f its containing object.</span><br style=3D"color:rgb(80,0,80);font-size:12=
..8000001907349px"><br style=3D"color:rgb(80,0,80);font-size:12.800000190734=
9px"><span style=3D"color:rgb(80,0,80);font-size:12.8000001907349px">Whethe=
r calling it "class" or something else, is really just bikesheddi=
ng. One should first determine its conceptual and technical properties befo=
re starting the naming debate. I don't claim to have all the answers wh=
en it comes to applying this concept to C++ and I don't claim there are=
n't C++ specificy problems that need solving. It just felt like the con=
cept wasn't explained very much and therfore caused ambiguity.</span><b=
r style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><br style=3D"co=
lor:rgb(80,0,80);font-size:12.8000001907349px"><span style=3D"color:rgb(80,=
0,80);font-size:12.8000001907349px">Here some hypothetical example code:</s=
pan><br style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><br style=
=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><span style=3D"color:r=
gb(80,0,80);font-size:12.8000001907349px">class A {</span><br style=3D"colo=
r:rgb(80,0,80);font-size:12.8000001907349px"><span style=3D"color:rgb(80,0,=
80);font-size:12.8000001907349px">=C2=A0 int x_data;</span><br style=3D"col=
or:rgb(80,0,80);font-size:12.8000001907349px"><span style=3D"color:rgb(80,0=
,80);font-size:12.8000001907349px">public:</span><br style=3D"color:rgb(80,=
0,80);font-size:12.8000001907349px"><span style=3D"color:rgb(80,0,80);font-=
size:12.8000001907349px">=C2=A0 inline struct x {</span><br style=3D"color:=
rgb(80,0,80);font-size:12.8000001907349px"><span style=3D"color:rgb(80,0,80=
);font-size:12.8000001907349px">=C2=A0 =C2=A0 x& operator=3D(int value)=
{</span><br style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><spa=
n style=3D"color:rgb(80,0,80);font-size:12.8000001907349px">=C2=A0 =C2=A0 =
=C2=A0 x_data =3D value; // Can access members of containing class</span><b=
r style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><span style=3D"=
color:rgb(80,0,80);font-size:12.8000001907349px">=C2=A0 =C2=A0 =C2=A0 retur=
n *this; // "this" can be impl converted A* <-> A::x*</span=
><br style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><span style=
=3D"color:rgb(80,0,80);font-size:12.8000001907349px">=C2=A0 =C2=A0 }</span>=
<br style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><span style=
=3D"color:rgb(80,0,80);font-size:12.8000001907349px">=C2=A0 =C2=A0 int oper=
ator int() const { return x_data; }</span><br style=3D"color:rgb(80,0,80);f=
ont-size:12.8000001907349px"><span style=3D"color:rgb(80,0,80);font-size:12=
..8000001907349px">=C2=A0 };</span><br style=3D"color:rgb(80,0,80);font-size=
:12.8000001907349px"><span style=3D"color:rgb(80,0,80);font-size:12.8000001=
907349px">};</span><br style=3D"color:rgb(80,0,80);font-size:12.80000019073=
49px"><span style=3D"color:rgb(80,0,80);font-size:12.8000001907349px">A a;<=
/span><br style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><span s=
tyle=3D"color:rgb(80,0,80);font-size:12.8000001907349px">a.x =3D 5; // Conv=
erted to a.x_operator=3D(5)</span><br style=3D"color:rgb(80,0,80);font-size=
:12.8000001907349px"><span style=3D"color:rgb(80,0,80);font-size:12.8000001=
907349px">cout << a.x; // Converted to a.x_operator_int()</span><br s=
tyle=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><br style=3D"color=
:rgb(80,0,80);font-size:12.8000001907349px"><span style=3D"color:rgb(80,0,8=
0);font-size:12.8000001907349px">Even though the setter returns A::x&, =
the inline class A::x can only ever exist as reference/pointer outside of A=
and is only copyable as part of its containing object, therefore it is not=
a proxy. To the compiler A* and A::x* are the same value, they only change=
what operations are allowed on the target. In this example A::x has no dat=
a members therefore it conceptually doesn't require storage and &a.=
x =3D=3D &b.x is only ever true if &a =3D=3D &b.</span><br styl=
e=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><br style=3D"color:rg=
b(80,0,80);font-size:12.8000001907349px"><span style=3D"color:rgb(80,0,80);=
font-size:12.8000001907349px">A different example where the inline class do=
es have members:</span><br style=3D"color:rgb(80,0,80);font-size:12.8000001=
907349px"><br style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><sp=
an style=3D"color:rgb(80,0,80);font-size:12.8000001907349px">class B {</spa=
n><br style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><span style=
=3D"color:rgb(80,0,80);font-size:12.8000001907349px">public:</span><br styl=
e=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><span style=3D"color:=
rgb(80,0,80);font-size:12.8000001907349px">=C2=A0 inline struct x {</span><=
br style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><span style=3D=
"color:rgb(80,0,80);font-size:12.8000001907349px">=C2=A0 =C2=A0 x& oper=
ator=3D(int value) { x_data =3D value; return *this; }</span><br style=3D"c=
olor:rgb(80,0,80);font-size:12.8000001907349px"><span style=3D"color:rgb(80=
,0,80);font-size:12.8000001907349px">=C2=A0 =C2=A0 int operator int() const=
{ return x_data; }</span><br style=3D"color:rgb(80,0,80);font-size:12.8000=
001907349px"><span style=3D"color:rgb(80,0,80);font-size:12.8000001907349px=
">=C2=A0 private:</span><br style=3D"color:rgb(80,0,80);font-size:12.800000=
1907349px"><span style=3D"color:rgb(80,0,80);font-size:12.8000001907349px">=
=C2=A0 =C2=A0 int data;</span><br style=3D"color:rgb(80,0,80);font-size:12.=
8000001907349px"><span style=3D"color:rgb(80,0,80);font-size:12.80000019073=
49px">=C2=A0 };</span><br style=3D"color:rgb(80,0,80);font-size:12.80000019=
07349px"><span style=3D"color:rgb(80,0,80);font-size:12.8000001907349px">=
=C2=A0 int foo() {</span><br style=3D"color:rgb(80,0,80);font-size:12.80000=
01907349px"><span style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"=
>=C2=A0 =C2=A0 return x.data * 2;</span><br style=3D"color:rgb(80,0,80);fon=
t-size:12.8000001907349px"><span style=3D"color:rgb(80,0,80);font-size:12.8=
000001907349px">=C2=A0 }</span><br style=3D"color:rgb(80,0,80);font-size:12=
..8000001907349px"><span style=3D"color:rgb(80,0,80);font-size:12.8000001907=
349px">};</span><br style=3D"color:rgb(80,0,80);font-size:12.8000001907349p=
x"><span style=3D"color:rgb(80,0,80);font-size:12.8000001907349px">B b;</sp=
an><br style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><span styl=
e=3D"color:rgb(80,0,80);font-size:12.8000001907349px">b.x =3D 5;</span><br =
style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><span style=3D"co=
lor:rgb(80,0,80);font-size:12.8000001907349px">cout << b.foo();</span=
><br style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"><br style=3D=
"color:rgb(80,0,80);font-size:12.8000001907349px"><span style=3D"color:rgb(=
80,0,80);font-size:12.8000001907349px">Besides a way to implement propertie=
s inline classes can serve to group membes/methods the same way a non-inlin=
e nested class currently does except it drops the requirement for a this me=
mber pointing to the parent. Even if there was a dedicated property keyword=
/whatever the implementation technique could be inline classes and thefore =
kept open for advanced users that want more than just the default getter/se=
tter implementation.</span><br style=3D"color:rgb(80,0,80);font-size:12.800=
0001907349px"><br style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"=
><span style=3D"color:rgb(80,0,80);font-size:12.8000001907349px">I hope thi=
s adequately explains what is meant by inline classes. It just seemed like =
it wasn't described very well and people were talking past each other.<=
/span><div style=3D"color:rgb(80,0,80);font-size:12.8000001907349px"></div>=
</blockquote><div>=C2=A0</div></div></div></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--001a11c3170a73c1d9051e8c1b91--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Sun, 30 Aug 2015 16:05:20 -0700 (PDT)
Raw View
------=_Part_2754_1889251212.1440975920543
Content-Type: multipart/alternative;
boundary="----=_Part_2755_1688867459.1440975920543"
------=_Part_2755_1688867459.1440975920543
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Sunday, August 30, 2015 at 3:13:11 PM UTC-4, Klaim - Jo=C3=ABl Lamotte w=
rote:
>
> On 30 August 2015 at 18:49, Nicol Bolas <jmck...@gmail.com <javascript:>>=
=20
> wrote:
>
>> On Sunday, August 30, 2015 at 10:05:49 AM UTC-4, Klaim - Jo=C3=ABl Lamot=
te=20
>> wrote:
>>>
>>> I think your inline class idea better have it's own discussion thread s=
o=20
>>> that exploring the concept can be focused on it.
>>>
>>
>> The thing it most needs is justification is why we need such a feature.=
=20
>> Can it do something more than give us property hack fixes?
>>
>>
> My suggestion was to discuss this outside the properties thread because=
=20
> it's impossible to follow with the several sub-discussions ongoing.
>
You haven't exactly made that easier. You didn't bother to explain what=20
inline classes are, or even link to the initial post talking about it. So=
=20
if someone isn't willing to comb through the properties thread, they're not=
=20
going to know what you're talking about.
=20
> Also, the "justification", however weak, have already been given in the=
=20
> discussion.
>
Different threads are different; that's the point of starting them. So if=
=20
you want to start a discussion purely about inline classes, then you need=
=20
to explain what they are and justify why they're an important feature.
Just like any proposal thread.
=20
> =20
>
>> A more useful thing to explore is the concept of empty member=20
>> optimizations (which would be necessary to make properties worthwhile wi=
th=20
>> these). It wouldn't be acceptable to have the standard suddenly break lo=
ts=20
>> of code by declaring that all empty classes that are members of other=20
>> classes no longer take up space.
>>
>> But would it be possible to decorate a class (using contextual keywords=
=20
>> ala `final`) such that the class would be considered to be truly empty. =
And=20
>> that such truly empty classes would not take up space in structs. If tha=
t=20
>> is possible, it could have a multitude of uses.
>>
>
>
> I fail to see the relationship with the initial subject or with the=20
> proposed inline class.
>
One of the principle failings of all properties-through-objects classes in=
=20
C++ is that the property is an object and must obey the rules of C++=20
objects. Like taking up space in the class that contains them.
Inline classes are defined to take up no space if they contain no members.=
=20
This was explicitly stated in the initial idea for them. So talking about=
=20
how to allow empty types to take up no space in other types is part and=20
parcel of that. Especially if it can be spun off into its own feature=20
(which is much more likely to pass).
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
------=_Part_2755_1688867459.1440975920543
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Sunday, August 30, 2015 at 3:13:11 PM UTC-4, Klaim - Jo=C3=ABl Lamotte w=
rote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8e=
x;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><di=
v class=3D"gmail_quote">On 30 August 2015 at 18:49, Nicol Bolas <span dir=
=3D"ltr"><<a href=3D"javascript:" target=3D"_blank" gdf-obfuscated-mailt=
o=3D"Yorc58iiBwAJ" rel=3D"nofollow" onmousedown=3D"this.href=3D'javascr=
ipt:';return true;" onclick=3D"this.href=3D'javascript:';return=
true;">jmck...@gmail.com</a>></span> wrote:<br><blockquote class=3D"gma=
il_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-le=
ft-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div di=
r=3D"ltr"><span>On Sunday, August 30, 2015 at 10:05:49 AM UTC-4, Klaim - Jo=
=C3=ABl Lamotte wrote:<blockquote class=3D"gmail_quote" style=3D"margin:0px=
0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);bor=
der-left-style:solid;padding-left:1ex"><div dir=3D"ltr">I think your inline=
class idea better have it's own discussion thread so that exploring th=
e concept can be focused on it.</div></blockquote></span><div><br>The thing=
it most needs is justification is why we need such a feature. Can it do so=
mething more than give us property hack fixes?<br><br></div></div></blockqu=
ote><div><br></div><div>My suggestion was to discuss this outside the prope=
rties thread because it's impossible to follow with the several sub-dis=
cussions ongoing.</div></div></div></div></blockquote><div><br>You haven=
9;t exactly made that easier. You didn't bother to explain what inline =
classes are, or even link to the initial post talking about it. So if someo=
ne isn't willing to comb through the properties thread, they're not=
going to know what you're talking about.<br>=C2=A0</div><blockquote cl=
ass=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px =
#ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><div class=3D"gmail_qu=
ote"><div></div><div>Also, the "justification", however weak, hav=
e already been given in the discussion.</div></div></div></div></blockquote=
><div><br>Different threads are different; that's the point of starting=
them. So if you want to start a discussion purely about inline classes, th=
en you need to explain what they are and justify why they're an importa=
nt feature.<br><br>Just like any proposal thread.<br>=C2=A0</div><blockquot=
e class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: =
1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><div class=3D"gmai=
l_quote"><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin=
:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204)=
;border-left-style:solid;padding-left:1ex"><div dir=3D"ltr"><div>A more use=
ful thing to explore is the concept of empty member optimizations (which wo=
uld be necessary to make properties worthwhile with these). It wouldn't=
be acceptable to have the standard suddenly break lots of code by declarin=
g that all empty classes that are members of other classes no longer take u=
p space.<br><br></div></div></blockquote><blockquote class=3D"gmail_quote" =
style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:r=
gb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr">=
<div>But would it be possible to decorate a class (using contextual keyword=
s ala `final`) such that the class would be considered to be truly empty. A=
nd that such truly empty classes would not take up space in structs. If tha=
t is possible, it could have a multitude of uses.<br></div></div></blockquo=
te><div><br></div><div><br></div><div><div>I fail to see the relationship w=
ith the initial subject or with the proposed inline class.</div></div></div=
></div></div></blockquote><div><br>One of the principle failings of all pro=
perties-through-objects classes in C++ is that the property is an object an=
d must obey the rules of C++ objects. Like taking up space in the class tha=
t contains them.<br><br>Inline classes are defined to take up no space if t=
hey contain no members. This was explicitly stated in the initial idea for =
them. So talking about how to allow empty types to take up no space in othe=
r types is part and parcel of that. Especially if it can be spun off into i=
ts own feature (which is much more likely to pass).<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_2755_1688867459.1440975920543--
------=_Part_2754_1889251212.1440975920543--
.
Author: Miro Knejp <miro.knejp@gmail.com>
Date: Mon, 31 Aug 2015 01:57:53 +0200
Raw View
Am 31.08.2015 um 01:05 schrieb Nicol Bolas:
>
> One of the principle failings of all properties-through-objects
> classes in C++ is that the property is an object and must obey the
> rules of C++ objects. Like taking up space in the class that contains
> them.
Why? You are begging the question here. *You* place this requirement on
these kinds of properties and *you* then conclude that the presented
solutions are impossible because they don't satisfy the requirements you
demand in the *current* language rules. Well, duh, a new language
feature obviously doesn't work with the current rules because otherwise
it wouldn't be a *new* feature to be added in a *future* version of the
language. The necessary rules can be adapted accordingly.
>
> Inline classes are defined to take up no space if they contain no
> members. This was explicitly stated in the initial idea for them. So
> talking about how to allow empty types to take up no space in other
> types is part and parcel of that. Especially if it can be spun off
> into its own feature (which is much more likely to pass).
Not all types are created equal. "enum types" are not the same as "class
types" and follow different rules, so it's not such a stretch to imagine
there might be some other "inline class type" category with its own
rulebook.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: =?UTF-8?Q?Klaim_=2D_Jo=C3=ABl_Lamotte?= <mjklaim@gmail.com>
Date: Mon, 31 Aug 2015 02:14:50 +0200
Raw View
--001a11c3f3866b9995051e9052e1
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On 31 August 2015 at 01:05, Nicol Bolas <jmckesson@gmail.com> wrote:
> On Sunday, August 30, 2015 at 3:13:11 PM UTC-4, Klaim - Jo=C3=ABl Lamotte=
wrote:
>>
>> On 30 August 2015 at 18:49, Nicol Bolas <jmck...@gmail.com> wrote:
>>
>>> On Sunday, August 30, 2015 at 10:05:49 AM UTC-4, Klaim - Jo=C3=ABl Lamo=
tte
>>> wrote:
>>>>
>>>> I think your inline class idea better have it's own discussion thread
>>>> so that exploring the concept can be focused on it.
>>>>
>>>
>>> The thing it most needs is justification is why we need such a feature.
>>> Can it do something more than give us property hack fixes?
>>>
>>>
>> My suggestion was to discuss this outside the properties thread because
>> it's impossible to follow with the several sub-discussions ongoing.
>>
>
> You haven't exactly made that easier. You didn't bother to explain what
> inline classes are, or even link to the initial post talking about it. So
> if someone isn't willing to comb through the properties thread, they're n=
ot
> going to know what you're talking about.
>
>
I did include Miro's explanation in the end of the first email of this
discussion thread.
> Also, the "justification", however weak, have already been given in the
>> discussion.
>>
>
> Different threads are different; that's the point of starting them. So if
> you want to start a discussion purely about inline classes, then you need
> to explain what they are and justify why they're an important feature.
>
> Just like any proposal thread.
>
>
I guess it would be good to focus on that anyway yes.
>
>>
>>> A more useful thing to explore is the concept of empty member
>>> optimizations (which would be necessary to make properties worthwhile w=
ith
>>> these). It wouldn't be acceptable to have the standard suddenly break l=
ots
>>> of code by declaring that all empty classes that are members of other
>>> classes no longer take up space.
>>>
>>> But would it be possible to decorate a class (using contextual keywords
>>> ala `final`) such that the class would be considered to be truly empty.=
And
>>> that such truly empty classes would not take up space in structs. If th=
at
>>> is possible, it could have a multitude of uses.
>>>
>>
>>
>> I fail to see the relationship with the initial subject or with the
>> proposed inline class.
>>
>
> One of the principle failings of all properties-through-objects classes i=
n
> C++ is that the property is an object and must obey the rules of C++
> objects. Like taking up space in the class that contains them.
>
> Inline classes are defined to take up no space if they contain no members=
..
> This was explicitly stated in the initial idea for them. So talking about
> how to allow empty types to take up no space in other types is part and
> parcel of that. Especially if it can be spun off into its own feature
> (which is much more likely to pass).
>
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
--001a11c3f3866b9995051e9052e1
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><br><div class=3D"gmail_extra"><br><div class=3D"gmail_quo=
te">On 31 August 2015 at 01:05, Nicol Bolas <span dir=3D"ltr"><<a href=
=3D"mailto:jmckesson@gmail.com" target=3D"_blank">jmckesson@gmail.com</a>&g=
t;</span> wrote:<br><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0=
.8ex;border-left:1px #ccc solid;padding-left:1ex">On Sunday, August 30, 20=
15 at 3:13:11 PM UTC-4, Klaim - Jo=C3=ABl Lamotte wrote:<span class=3D""><b=
lockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-=
left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div><div class=3D"g=
mail_quote">On 30 August 2015 at 18:49, Nicol Bolas <span dir=3D"ltr"><<=
a rel=3D"nofollow">jmck...@gmail.com</a>></span> wrote:<br><blockquote c=
lass=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1p=
x;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1=
ex"><div dir=3D"ltr"><span>On Sunday, August 30, 2015 at 10:05:49 AM UTC-4,=
Klaim - Jo=C3=ABl Lamotte wrote:<blockquote class=3D"gmail_quote" style=3D=
"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,2=
04,204);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr">I think =
your inline class idea better have it's own discussion thread so that e=
xploring the concept can be focused on it.</div></blockquote></span><div><b=
r>The thing it most needs is justification is why we need such a feature. C=
an it do something more than give us property hack fixes?<br><br></div></di=
v></blockquote><div><br></div><div>My suggestion was to discuss this outsid=
e the properties thread because it's impossible to follow with the seve=
ral sub-discussions ongoing.</div></div></div></div></blockquote></span><di=
v><br>You haven't exactly made that easier. You didn't bother to ex=
plain what inline classes are, or even link to the initial post talking abo=
ut it. So if someone isn't willing to comb through the properties threa=
d, they're not going to know what you're talking about.<br>=C2=A0</=
div></blockquote><div><br></div><div>I did include Miro's explanation i=
n the end of the first email of this discussion thread.=C2=A0</div><div>=C2=
=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;borde=
r-left:1px #ccc solid;padding-left:1ex"><span class=3D""><blockquote class=
=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc s=
olid;padding-left:1ex"><div dir=3D"ltr"><div><div class=3D"gmail_quote"><di=
v></div><div>Also, the "justification", however weak, have alread=
y been given in the discussion.</div></div></div></div></blockquote></span>=
<div><br>Different threads are different; that's the point of starting =
them. So if you want to start a discussion purely about inline classes, the=
n you need to explain what they are and justify why they're an importan=
t feature.<br><br>Just like any proposal thread.<br>=C2=A0</div></blockquot=
e><div><br></div><div>I guess it would be good to focus on that anyway yes.=
</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0 =
0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class=3D""><blo=
ckquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-le=
ft:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div><div class=3D"gma=
il_quote"><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margi=
n:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204=
);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr"><div>A more us=
eful thing to explore is the concept of empty member optimizations (which w=
ould be necessary to make properties worthwhile with these). It wouldn'=
t be acceptable to have the standard suddenly break lots of code by declari=
ng that all empty classes that are members of other classes no longer take =
up space.<br><br></div></div></blockquote><blockquote class=3D"gmail_quote"=
style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:=
rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr"=
><div>But would it be possible to decorate a class (using contextual keywor=
ds ala `final`) such that the class would be considered to be truly empty. =
And that such truly empty classes would not take up space in structs. If th=
at is possible, it could have a multitude of uses.<br></div></div></blockqu=
ote><div><br></div><div><br></div><div><div>I fail to see the relationship =
with the initial subject or with the proposed inline class.</div></div></di=
v></div></div></blockquote></span><div><br>One of the principle failings of=
all properties-through-objects classes in C++ is that the property is an o=
bject and must obey the rules of C++ objects. Like taking up space in the c=
lass that contains them.<br><br>Inline classes are defined to take up no sp=
ace if they contain no members. This was explicitly stated in the initial i=
dea for them. So talking about how to allow empty types to take up no space=
in other types is part and parcel of that. Especially if it can be spun of=
f into its own feature (which is much more likely to pass).</div></blockquo=
te><div><br></div><div><br></div></div></div></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--001a11c3f3866b9995051e9052e1--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Sun, 30 Aug 2015 18:08:42 -0700 (PDT)
Raw View
------=_Part_473_832088542.1440983323013
Content-Type: multipart/alternative;
boundary="----=_Part_474_1464405351.1440983323013"
------=_Part_474_1464405351.1440983323013
Content-Type: text/plain; charset=UTF-8
On Sunday, August 30, 2015 at 7:56:29 PM UTC-4, Miro Knejp wrote:
>
> Am 31.08.2015 um 01:05 schrieb Nicol Bolas:
> >
> > One of the principle failings of all properties-through-objects
> > classes in C++ is that the property is an object and must obey the
> > rules of C++ objects. Like taking up space in the class that contains
> > them.
> Why?
.... Are you seriously asking why it's undesirable by users for a member
that doesn't contain anything, one that exists *solely for syntactic sugar*,
to take up space in a type? This is genuinely a question you need an answer
to?
Here's the thing: you can make an implementation of properties in C++ *right
now*. You can define a property class and give it a bunch of overloaded
operators that make it work in almost exactly the way you want inline
classes to work.
The problem is that these implementations impose *overhead*, both syntactic
and semantic. They break trivial copyability, since they need a `this`
pointer to the owner. Getting access to that `this` pointer requires rather
unnatural code (when implementing your getter/setter). Implementing
getters/setters often requires lambdas or other syntax kludges. Properties
make the class bigger needlessly. And so forth.
This overhead, both syntactic and semantic, makes people disinclined to use
them for properties. That's why you see people still asking for properties
in C++; because the library solution is a non-starter.
Inline classes were proposed for the sole purpose of *avoiding* that
overhead. Their job was to make a library solution for properties viable.
Therefore, if they can't even make properties work without overhead... what
good are they?
So yes, empty inline class members taking up space is a problem.
You are begging the question here. *You* place this requirement on
> these kinds of properties and *you* then conclude that the presented
>
solutions are impossible because they don't satisfy the requirements you
> demand in the *current* language rules.
I never said that they're impossible. What I'm saying is that you are
arbitrarily ransacking the language. And thus far, the only justification
provided for this is to make it easier to implement a niche feature. And
even with this language ransacking, the abstraction leaks; the fact that a
property is being used is visible to the user.
I don't think that inline classes are worth the hassle of making them
actually *work*. Or more to the point, you have not yet proven that this
inline clases are *sufficiently useful* to justify the sheer number of
language changes that will be necessary in order to make this feature work.
Language features aren't free, and the more of the language the feature
affects, the more it costs. You have to pay for them by having the feature
provide something that is genuinely useful to people. So please, give some
justifications for what problems users have that this feature will solve.
And no, "we can get properties" doesn't count. We already have a thread for
that.
> Well, duh, a new language
> feature obviously doesn't work with the current rules because otherwise
> it wouldn't be a *new* feature to be added in a *future* version of the
> language. The necessary rules can be adapted accordingly.
>
What you seem to be saying is that the language ramifications of a proposal
should not even be *considered* by the people proposing it. That we should
simply ignore the innumerable *basic* rules of C++ that have to be worked
around in order for the proposal to work.
I don't agree with that. If it takes 5 minutes of thought to break a
proposal, then it was not sufficiently well thought out enough to make in
the first place.
>
> > Inline classes are defined to take up no space if they contain no
> > members. This was explicitly stated in the initial idea for them. So
> > talking about how to allow empty types to take up no space in other
> > types is part and parcel of that. Especially if it can be spun off
> > into its own feature (which is much more likely to pass).
> Not all types are created equal. "enum types" are not the same as "class
> types" and follow different rules, so it's not such a stretch to imagine
> there might be some other "inline class type" category with its own
> rulebook.
>
OK so... what *is* the rulebook?
This is your idea. This is your proposal (not necessarily all you
personally. I'm invoking the "royal you", meaning those who want it). What
should this "own rulebook" actually be?
Because thus far, we haven't seen one. Oh, we've seen bits of one. We have
some general notions. But we have not yet seen a fully-formed idea, one
that actually goes through at least the obvious language changes that need
to happen for them to work.
That's why this shouldn't have gotten a separate thread; there isn't a
proposal yet.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_474_1464405351.1440983323013
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Sunday, August 30, 2015 at 7:56:29 PM UTC-4, Miro Knejp=
wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.=
8ex;border-left: 1px #ccc solid;padding-left: 1ex;">Am 31.08.2015 um 01:05 =
schrieb Nicol Bolas:
<br>>
<br>> One of the principle failings of all properties-through-objects=20
<br>> classes in C++ is that the property is an object and must obey the=
=20
<br>> rules of C++ objects. Like taking up space in the class that conta=
ins=20
<br>> them.
<br>Why?</blockquote><div><br>... Are you seriously asking why it's und=
esirable by users for a member that doesn't contain anything, one that =
exists <i>solely for syntactic sugar</i>, to take up space in a type? This =
is genuinely a question you need an answer to?<br><br>Here's the thing:=
you can make an implementation of properties in C++ <i>right now</i>. You =
can define a property class and give it a bunch of overloaded operators tha=
t make it work in almost exactly the way you want inline classes to work.<b=
r><br>The problem is that these implementations impose <i>overhead</i>, bot=
h syntactic and semantic. They break trivial copyability, since they need a=
`this` pointer to the owner. Getting access to that `this` pointer require=
s rather unnatural code (when implementing your getter/setter). Implementin=
g getters/setters often requires lambdas or other syntax kludges. Propertie=
s make the class bigger needlessly. And so forth.<br><br>This overhead, bot=
h syntactic and semantic, makes people disinclined to use them for properti=
es. That's why you see people still asking for properties in C++; becau=
se the library solution is a non-starter.<br><br>Inline classes were propos=
ed for the sole purpose of <i>avoiding</i> that overhead. Their job was to =
make a library solution for properties viable. Therefore, if they can't=
even make properties work without overhead... what good are they?<br><br>S=
o yes, empty inline class members taking up space is a problem.<br><br></di=
v><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;b=
order-left: 1px #ccc solid;padding-left: 1ex;">You are begging the question=
here. *You* place this requirement on=20
<br>these kinds of properties and *you* then conclude that the presented=C2=
=A0=20
<br></blockquote><blockquote class=3D"gmail_quote" style=3D"margin: 0;margi=
n-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">solutions are=
impossible because they don't satisfy the requirements you=20
<br>demand in the *current* language rules.</blockquote><div><br>I never sa=
id that they're impossible. What I'm saying is that you are arbitra=
rily ransacking the language. And thus far, the only justification provided=
for this is to make it easier to implement a niche feature. And even with =
this language ransacking, the abstraction leaks; the fact that a property i=
s being used is visible to the user.<br><br>I don't think that inline c=
lasses are worth the hassle of making them actually <i>work</i>. Or more to=
the point, you have not yet proven that this inline clases are <i>sufficie=
ntly useful</i> to justify the sheer number of language changes that will b=
e necessary in order to make this feature work.<br><br>Language features ar=
en't free, and the more of the language the feature affects, the more i=
t costs. You have to pay for them by having the feature provide something t=
hat is genuinely useful to people. So please, give some justifications for =
what problems users have that this feature will solve.<br><br>And no, "=
;we can get properties" doesn't count. We already have a thread fo=
r that.<br>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0=
;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">Well, d=
uh, a new language=20
<br>feature obviously doesn't work with the current rules because other=
wise=20
<br>it wouldn't be a *new* feature to be added in a *future* version of=
the=20
<br>language. The necessary rules can be adapted accordingly.<br></blockquo=
te><div><br>What you seem to be saying is that the language ramifications o=
f a proposal should not even be <i>considered</i> by the people proposing i=
t. That we should simply ignore the innumerable <i>basic</i> rules of C++ t=
hat have to be worked around in order for the proposal to work.<br><br>I do=
n't agree with that. If it takes 5 minutes of thought to break a propos=
al, then it was not sufficiently well thought out enough to make in the fir=
st place.<br><br></div><blockquote class=3D"gmail_quote" style=3D"margin: 0=
;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">>
<br>> Inline classes are defined to take up no space if they contain no=
=20
<br>> members. This was explicitly stated in the initial idea for them. =
So=20
<br>> talking about how to allow empty types to take up no space in othe=
r=20
<br>> types is part and parcel of that. Especially if it can be spun off=
=20
<br>> into its own feature (which is much more likely to pass).
<br>Not all types are created equal. "enum types" are not the sam=
e as "class=20
<br>types" and follow different rules, so it's not such a stretch =
to imagine=20
<br>there might be some other "inline class type" category with i=
ts own=20
<br>rulebook.<br></blockquote><div><br>OK so... what <i>is</i> the rulebook=
?<br><br>This is your idea. This is your proposal (not necessarily all you =
personally. I'm invoking the "royal you", meaning those who w=
ant it). What should this "own rulebook" actually be?<br><br>Beca=
use thus far, we haven't seen one. Oh, we've seen bits of one. We h=
ave some general notions. But we have not yet seen a fully-formed idea, one=
that actually goes through at least the obvious language changes that need=
to happen for them to work.<br><br>That's why this shouldn't have =
gotten a separate thread; there isn't a proposal yet.<br></div></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_474_1464405351.1440983323013--
------=_Part_473_832088542.1440983323013--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Mon, 31 Aug 2015 14:53:56 -0400
Raw View
(Start of original thread:
http://permalink.gmane.org/gmane.comp.lang.c++.isocpp.proposals/20049)
On 2015-08-30 19:05, Nicol Bolas wrote:
> One of the principle failings of all properties-through-objects classes in
> C++ is that the property is an object and must obey the rules of C++
> objects. Like taking up space in the class that contains them.
>
> Inline classes are defined to take up no space if they contain no members.
> This was explicitly stated in the initial idea for them.
Wrong:
> On 2015-08-26 13:54, Matthew Woehlke wrote:
>> An inline class [...] has no storage
I didn't said they "take up no space /if they contain no members/"
(emphasis added). I said they take up no space, *period*.
>> This inline class can contain member definitions as usual.
Maybe this is the cause of the confusion? I mean member *functions*,
there, not *data* members. (Data members don't have "definitions", but I
can see where that could be unclear.)
Also, no virtual members. (Come to think of it, no ctors/dtors, either,
which actually clarifies several issues. Those don't make sense, since
there is no storage to create or destroy.)
That said, static (data) members should be no problem.
> So talking about how to allow empty types to take up no space in
> other types is part and parcel of that. Especially if it can be spun
> off into its own feature (which is much more likely to pass).
You haven't explained why inline classes aren't usable for these other
purposes.
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Bjorn Reese <breese@mail1.stofanet.dk>
Date: Mon, 31 Aug 2015 21:25:58 +0200
Raw View
I frequently come across use-cases where inline classes can be useful.
One use-case is to compensate for the lack of partial template
specialization of class member functions. A workaround is to introduce
a functor with static member functions, which are passed the class
instance as a parameter, because the functor needs to operate on the
class member variables. For example:
class value
{
public:
template <typename T>
T convert() const;
};
Say that need a specialization for value::convert<T>() for integral
types. This can be done via the following functor:
template <typename T, typename Enable = void>
struct value_functor {};
template <typename T>
struct value_functor<T, enable_if<is_integral<T>>::type>
{
static T convert(const value& v)
{
// Do integral conversion here
}
};
This is invoked as follows:
template <typename T>
T value::convert() const
{
return value_functor<ReturnType>::convert(*this);
}
Furthermore, if value_functor must access private members in the value
class, then it must become a friend of the class.
An inline class, as I have understood it, will handle the above case
more elegantly.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Mon, 31 Aug 2015 13:35:06 -0700 (PDT)
Raw View
------=_Part_924_1919497515.1441053306729
Content-Type: multipart/alternative;
boundary="----=_Part_925_44972160.1441053306729"
------=_Part_925_44972160.1441053306729
Content-Type: text/plain; charset=UTF-8
On Monday, August 31, 2015 at 3:26:01 PM UTC-4, Bjorn Reese wrote:
>
> I frequently come across use-cases where inline classes can be useful.
>
> One use-case is to compensate for the lack of partial template
> specialization of class member functions. A workaround is to introduce
> a functor with static member functions, which are passed the class
> instance as a parameter, because the functor needs to operate on the
> class member variables. For example:
>
> class value
> {
> public:
> template <typename T>
> T convert() const;
> };
>
> Say that need a specialization for value::convert<T>() for integral
> types. This can be done via the following functor:
>
> template <typename T, typename Enable = void>
> struct value_functor {};
>
> template <typename T>
> struct value_functor<T, enable_if<is_integral<T>>::type>
> {
> static T convert(const value& v)
> {
> // Do integral conversion here
> }
> };
>
> This is invoked as follows:
>
> template <typename T>
> T value::convert() const
> {
> return value_functor<ReturnType>::convert(*this);
> }
>
> Furthermore, if value_functor must access private members in the value
> class, then it must become a friend of the class.
>
> An inline class, as I have understood it, will handle the above case
> more elegantly.
>
Please show what the inline class equivalent of this code would look like.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_925_44972160.1441053306729
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Monday, August 31, 2015 at 3:26:01 PM UTC-4, Bjorn Reese wrote:<blockquo=
te class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left:=
1px #ccc solid;padding-left: 1ex;">I frequently come across use-cases wher=
e inline classes can be useful.
<br>
<br>One use-case is to compensate for the lack of partial template
<br>specialization of class member functions. A workaround is to introduce
<br>a functor with static member functions, which are passed the class
<br>instance as a parameter, because the functor needs to operate on the
<br>class member variables. For example:
<br>
<br>=C2=A0 =C2=A0class value
<br>=C2=A0 =C2=A0{
<br>=C2=A0 =C2=A0public:
<br>=C2=A0 =C2=A0 =C2=A0template <typename T>
<br>=C2=A0 =C2=A0 =C2=A0T convert() const;
<br>=C2=A0 =C2=A0};
<br>
<br>Say that need a specialization for value::convert<T>() for integr=
al
<br>types. This can be done via the following functor:
<br>
<br>=C2=A0 =C2=A0template <typename T, typename Enable =3D void>
<br>=C2=A0 =C2=A0struct value_functor {};
<br>
<br>=C2=A0 =C2=A0template <typename T>
<br>=C2=A0 =C2=A0struct value_functor<T, enable_if<is_integral<T&g=
t;>::<wbr>type>
<br>=C2=A0 =C2=A0{
<br>=C2=A0 =C2=A0 =C2=A0static T convert(const value& v)
<br>=C2=A0 =C2=A0 =C2=A0{
<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 // Do integral conversion here
<br>=C2=A0 =C2=A0 =C2=A0}
<br>=C2=A0 =C2=A0};
<br>
<br>This is invoked as follows:
<br>
<br>=C2=A0 =C2=A0template <typename T>
<br>=C2=A0 =C2=A0T value::convert() const
<br>=C2=A0 =C2=A0{
<br>=C2=A0 =C2=A0 =C2=A0return value_functor<ReturnType>::<wbr>conver=
t(*this);
<br>=C2=A0 =C2=A0}
<br>
<br>Furthermore, if value_functor must access private members in the value
<br>class, then it must become a friend of the class.
<br>
<br>An inline class, as I have understood it, will handle the above case
<br>more elegantly.<br></blockquote><div><br>Please show what the inline cl=
ass equivalent of this code would look like.<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_925_44972160.1441053306729--
------=_Part_924_1919497515.1441053306729--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Mon, 31 Aug 2015 13:42:39 -0700 (PDT)
Raw View
------=_Part_1652_849393318.1441053759923
Content-Type: multipart/alternative;
boundary="----=_Part_1653_497344196.1441053759923"
------=_Part_1653_497344196.1441053759923
Content-Type: text/plain; charset=UTF-8
On Monday, August 31, 2015 at 2:54:18 PM UTC-4, Matthew Woehlke wrote:
>
> (Start of original thread:
> http://permalink.gmane.org/gmane.comp.lang.c++.isocpp.proposals/20049)
>
> On 2015-08-30 19:05, Nicol Bolas wrote:
> > One of the principle failings of all properties-through-objects classes
> in
> > C++ is that the property is an object and must obey the rules of C++
> > objects. Like taking up space in the class that contains them.
> >
> > Inline classes are defined to take up no space if they contain no
> members.
> > This was explicitly stated in the initial idea for them.
>
> Wrong:
>
> > On 2015-08-26 13:54, Matthew Woehlke wrote:
> >> An inline class [...] has no storage
>
> I didn't said they "take up no space /if they contain no members/"
> (emphasis added). I said they take up no space, *period*.
>
Either way you say it, it leads to one fact:
The language must define the possibility of a member object that has no
space. This is not a possibility that existed in C++ before, so inline
classes must introduce that concept in order for them to work. It has to
work through all of the problems surrounding allowing objects in other
objects to take up no space.
And if you're going to go through the trouble of defining the concept, I
see no reason not to extend it to more arbitrary classes.
It's the same amount of work either way. It's just that, if its a separate
construct, then it can be *more* useful, since it's not bound to the
limitations of inline classes.
> So talking about how to allow empty types to take up no space in
> > other types is part and parcel of that. Especially if it can be spun
> > off into its own feature (which is much more likely to pass).
>
> You haven't explained why inline classes aren't usable for these other
> purposes.
>
Because inline classes are *explicitly bound* to the classes that they are
nested within. And I quote (emphasis added):
> An inline class is a special case of a *nested class-type* which itself
has no storage, and for which "this" refers to the parent class which must
contain the inline class as a member.
If I just want a freestanding class that will take up no room, regardless
of which class it is used within, I can't do that with inline classes.
Since it's not a nested class.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_1653_497344196.1441053759923
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<br><br>On Monday, August 31, 2015 at 2:54:18 PM UTC-4, Matthew Woehlke wro=
te:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;=
border-left: 1px #ccc solid;padding-left: 1ex;">(Start of original thread:
<br><a href=3D"http://permalink.gmane.org/gmane.comp.lang.c++.isocpp.propos=
als/20049" target=3D"_blank" rel=3D"nofollow" onmousedown=3D"this.href=3D&#=
39;http://www.google.com/url?q\75http%3A%2F%2Fpermalink.gmane.org%2Fgmane.c=
omp.lang.c%2B%2B.isocpp.proposals%2F20049\46sa\75D\46sntz\0751\46usg\75AFQj=
CNGlUMZcFB6gaDvuJiddog4Fvp1LlQ';return true;" onclick=3D"this.href=3D&#=
39;http://www.google.com/url?q\75http%3A%2F%2Fpermalink.gmane.org%2Fgmane.c=
omp.lang.c%2B%2B.isocpp.proposals%2F20049\46sa\75D\46sntz\0751\46usg\75AFQj=
CNGlUMZcFB6gaDvuJiddog4Fvp1LlQ';return true;">http://permalink.gmane.or=
g/<wbr>gmane.comp.lang.c++.isocpp.<wbr>proposals/20049</a>)
<br>
<br>On 2015-08-30 19:05, Nicol Bolas wrote:
<br>> One of the principle failings of all properties-through-objects cl=
asses in=20
<br>> C++ is that the property is an object and must obey the rules of C=
++=20
<br>> objects. Like taking up space in the class that contains them.
<br>>=20
<br>> Inline classes are defined to take up no space if they contain no =
members.=20
<br>> This was explicitly stated in the initial idea for them.
<br>
<br>Wrong:
<br>
<br>> On 2015-08-26 13:54, Matthew Woehlke wrote:
<br>>> An inline class [...] has no storage
<br>
<br>I didn't said they "take up no space /if they contain no membe=
rs/"
<br>(emphasis added). I said they take up no space, *period*.<br></blockquo=
te><div><br>Either way you say it, it leads to one fact:<br><br>The languag=
e must define the possibility of a member object that has no space. This is=
not a possibility that existed in C++ before, so inline classes must intro=
duce that concept in order for them to work. It has to work through all of =
the problems surrounding allowing objects in other objects to take up no sp=
ace.<br><br>And if you're going to go through the trouble of defining t=
he concept, I see no reason not to extend it to more arbitrary classes.<br>=
<br>It's the same amount of work either way. It's just that, if its=
a separate construct, then it can be <i>more</i> useful, since it's no=
t bound to the limitations of inline classes.<br><br></div><blockquote clas=
s=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #c=
cc solid;padding-left: 1ex;">
> So talking about how to allow empty types to take up no space in=20
<br>> other types is part and parcel of that. Especially if it can be sp=
un
<br>> off into its own feature (which is much more likely to pass).
<br>
<br>You haven't explained why inline classes aren't usable for thes=
e other
<br>purposes.<br></blockquote><div><br>Because inline classes are <i>explic=
itly bound</i> to the classes that they are nested within. And I quote (emp=
hasis added):<br><br>> An inline class is a special case of a <b>nested =
class-type</b> which itself has no storage, and for which "this" =
refers to the parent class which must contain the inline class as a member.=
<br><br>If I just want a freestanding class that will take up no room, rega=
rdless of which class it is used within, I can't do that with inline cl=
asses. Since it's not a nested class.<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_1653_497344196.1441053759923--
------=_Part_1652_849393318.1441053759923--
.
Author: Thiago Macieira <thiago@macieira.org>
Date: Mon, 31 Aug 2015 14:01:25 -0700
Raw View
On Monday 31 August 2015 14:53:56 Matthew Woehlke wrote:
> I didn't said they "take up no space /if they contain no members/"
> (emphasis added). I said they take up no space, *period*.
The problem with taking up no space is that you can't have two of them in a
class, because their (empty) subobjects would have the same address. You
simply can't have that, period.
It sounds to me that the actual class would just be an "adaptor" or "view" of
the outer class. For example, an implementation could make it so the object
carries the "this" pointer to the outer. Something like:
class Outer
{
int m_value;
public:
// syntax potentially conflicts with inline variables
inline struct {
decltype(*this) &operator=(int newValue)
{ this->m_value = value; }
operator int() const { return this->m_value; }
void *operator &() const = delete;
} value;
/* if the syntax is a problem, we can use:
auto value = inline struct { ... };
*/
};
Where the *real* inline struct would be:
struct _RealInline { Outer *this; };
But if I were implementing this, I would make the ABI avoid the double
indirection and not store a pointer to the this pointer, but the pointer
directly, as you would an Outer *.
Questions:
* can you actually obtain a reference to the property? Note that
decltype(*this): does that expand to Outer or to the inner type?
* if it's to the outer type, then this could be surprising:
(obj.value = 1) = 2;
* if it's to the inner type, then you can bind a reference to it:
decltype(auto) v = (obj.value = 1);
and form a pointer even though operator& was disallowed:
auto ptr = std::addressof(v);
* a third option is for operator= to return a prvalue (int) or void. Note
that decltype(*this) in that context is currently not allowed, so you
couldn't get the type of an anonymous struct that easily (but not
impossible).
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Mon, 31 Aug 2015 17:21:23 -0400
Raw View
On 2015-08-31 17:01, Thiago Macieira wrote:
> On Monday 31 August 2015 14:53:56 Matthew Woehlke wrote:
>> I didn't said they "take up no space /if they contain no members/"
>> (emphasis added). I said they take up no space, *period*.
>
> The problem with taking up no space is that you can't have two of them in a
> class, because their (empty) subobjects would have the same address.
Er... yes. They don't act like regular "objects" in the sense the
language standard uses that term.
> It sounds to me that the actual class would just be an "adaptor" or "view" of
> the outer class.
Well... yes! That's exactly what I was getting at with the color-type
example.
> For example, an implementation could make it so the object
> carries the "this" pointer to the outer. Something like:
>
> class Outer
> {
> int m_value;
> public:
> // syntax potentially conflicts with inline variables
> inline struct {
> decltype(*this) &operator=(int newValue)
> { this->m_value = value; }
-Werror=return-type! You forgot 'return *this;' ;-). (Which I mention
mainly because I would further hope that you can just write the return
type as 'auto' and let it be deduced from the 'return *this;'.)
> operator int() const { return this->m_value; }
> void *operator &() const = delete;
> } value;
>
> /* if the syntax is a problem, we can use:
> auto value = inline struct { ... };
> */
Yes, that should also work, and might be preferable stylistically. If we
have to allow only that form, I have no problem. (Or... using some
totally other syntax, as I've mentioned repeatedly :-).)
> };
>
> Questions:
> * can you actually obtain a reference to the property? Note that
> decltype(*this): does that expand to Outer or to the inner type?
Yes, you can obtain a reference to the property / inline struct. The
type is Outer::Inner&, but the memory address is the same as the Outer&.
> * if it's to the inner type, then you can bind a reference to it:
> decltype(auto) v = (obj.value = 1);
> and form a pointer even though operator& was disallowed:
> auto ptr = std::addressof(v);
Er... where/why was operator& disallowed? I don't recall doing so
(though I admit my recollection may be faulty). It seems that one should
be allowed to do so; the type of the pointer is Outer::Inner*, with bits
equal to the Outer*.
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Bjorn Reese <breese@mail1.stofanet.dk>
Date: Mon, 31 Aug 2015 23:33:13 +0200
Raw View
On 08/31/2015 10:35 PM, Nicol Bolas wrote:
> Please show what the inline class equivalent of this code would look like.
I just provided a use-case. I will leave the syntax of inline classes
to you guys.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Mon, 31 Aug 2015 14:37:11 -0700 (PDT)
Raw View
------=_Part_3758_1759904484.1441057031574
Content-Type: multipart/alternative;
boundary="----=_Part_3759_905101634.1441057031574"
------=_Part_3759_905101634.1441057031574
Content-Type: text/plain; charset=UTF-8
On Monday, August 31, 2015 at 5:33:17 PM UTC-4, Bjorn Reese wrote:
>
> On 08/31/2015 10:35 PM, Nicol Bolas wrote:
>
> > Please show what the inline class equivalent of this code would look
> like.
>
> I just provided a use-case. I will leave the syntax of inline classes
> to you guys.
>
>
The code you showed was what you had to do before inner classes. I don't
understand how that would translate to inner classes. So I'm asking you to
show how they would be used to provide equivalent functionality.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_3759_905101634.1441057031574
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><br><br>On Monday, August 31, 2015 at 5:33:17 PM UTC-4, Bj=
orn Reese wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin=
-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">On 08/31/2015 =
10:35 PM, Nicol Bolas wrote:
<br>
<br>> Please show what the inline class equivalent of this code would lo=
ok like.
<br>
<br>I just provided a use-case. I will leave the syntax of inline classes
<br>to you guys.
<br>
<br></blockquote><div><br>The code you showed was what you had to do before=
inner classes. I don't understand how that would translate to inner cl=
asses. So I'm asking you to show how they would be used to provide equi=
valent functionality.<br></div></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_3759_905101634.1441057031574--
------=_Part_3758_1759904484.1441057031574--
.
Author: Thiago Macieira <thiago@macieira.org>
Date: Mon, 31 Aug 2015 15:21:28 -0700
Raw View
On Monday 31 August 2015 17:21:23 Matthew Woehlke wrote:
> > decltype(*this) &operator=(int newValue)
> > { this->m_value = value; }
>
> -Werror=return-type! You forgot 'return *this;' ;-). (Which I mention
> mainly because I would further hope that you can just write the return
> type as 'auto' and let it be deduced from the 'return *this;'.)
But if this is Outer*, return *this would be Outer&, not Outer::Inner&.
> > Questions:
> > * can you actually obtain a reference to the property? Note that
> > decltype(*this): does that expand to Outer or to the inner type?
>
> Yes, you can obtain a reference to the property / inline struct. The
> type is Outer::Inner&, but the memory address is the same as the Outer&.
>
> > * if it's to the inner type, then you can bind a reference to it:
> > decltype(auto) v = (obj.value = 1);
> >
> > and form a pointer even though operator& was disallowed:
> > auto ptr = std::addressof(v);
>
> Er... where/why was operator& disallowed?
Because I added:
void *operator &() const = delete;
> I don't recall doing so
> (though I admit my recollection may be faulty). It seems that one should
> be allowed to do so; the type of the pointer is Outer::Inner*, with bits
> equal to the Outer*.
Another question:
* How do I call a function in Outer that has the same name as one in the
inner struct? Suggestion:
void set(int newValue)
{ Outer::set(newValue); }
In other words, the inner, inline class behaves as if it derived from the
outer class.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Thiago Macieira <thiago@macieira.org>
Date: Mon, 31 Aug 2015 15:22:43 -0700
Raw View
On Monday 31 August 2015 15:21:28 Thiago Macieira wrote:
> In other words, the inner, inline class behaves as if it derived from the
> outer class.
Protected inheritance.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Miro Knejp <miro.knejp@gmail.com>
Date: Tue, 1 Sep 2015 03:02:54 +0200
Raw View
This is a multi-part message in MIME format.
--------------020008080104070705020706
Content-Type: text/plain; charset=UTF-8; format=flowed
Am 31.08.2015 um 20:53 schrieb Matthew Woehlke:
> I didn't said they "take up no space /if they contain no members/"
> (emphasis added). I said they take up no space, *period*.
It was me who said that and I believe this is an unnecessary limitation.
Let me try to recap all that was being suggested or discussed so far in
this and the property thread and propose some solutions of my own. This
may not be an exhaustive list.
*(1)* An inline subobject is only instantiable and copyable as part of
the containing object besides user defined conversion functions. Rules
for compiler-generated functions for the containing class behave as-if
the inline subobject was a regular subobject. Rules for deriving
properties like standard layout and triviality behave as-if the inline
subobject was a regular subobject.
*(2)* Name lookup in the inline subobject behaves as-if the inline
subobject was derived from the containing class.
*(3)* Should it be possible to obtain a pointer/reference to the inline
subobject itself?
Pro:
* It can be passed as reference/pointer around on its own and be
involved in type deduction as long as it is not attempted to make a copy
of it outside of its containing class type.
Con:
* If empty inline subobjects have no storage then multiple instances can
have the same address.
* In the context of properties (under the assumption they are
implemented with this technique) it is potentially a leaking
abstraction. Other languages don't allow you to pass around the actual
property itself, only the result of their getter. But then, these
languages usually don't have the concept of references. It might be an
interesting tool to pass a property by-reference to a function and have
that function operate on the property instead of only its value. But it
poses the ambiguity of when to deduce the property object and when the
property's value.
*(4)* Should empty inline subobjects have storage?
Pro: zero overhead
Con: see (3)
*(5)* Should separate inline subobjects be allowed to have the same type?
Pro: It's convenient.
Con:
* If they aren't empty and access their state in member functions the
compiler must find a way to distinguish them. Either by storing a hidden
offset or by instantiating different actual types. Both options aren't
zero overhead either in object size or code size.
* If every inline subobject has a different type and (3) is resolved as
yes then it is possible to safely static_cast between the containing
instance and the inline subobject.
*(6)* It should be possible to declare re-usable inline classes outside
of the containing class. This means these inline classes defined at
namespace scope must be based on template instantiations as they are
only complete types when embedded in a containing class and final name
lookup and offset-patching can take place.
Here are my attempts at solving some of the above problems.
Keep in mind that this is largely pseudocode so please don't get riled
up on the syntax again.
*(6)* I'll give this a try first because it is not dependent on others:
inline class P {
int i;
public:
void set(int j) { i = j; }
int get() { return i; }
void bar() { this->baz(); }
};
In a definition like the above "this" is always dependent because its
type is unknown until the inline class is embedded in an actual
containing class. This is the same name lookup behavior as in classes
with a dependent base class, making it consistent with existing practice
and the suggested name lookup for inline subobjects. It is basically
syntax for compiler-generated CRTP (which is a useful pattern on its own
and this might actually make it more accessible).
It is then used like
class A {
P x; // Instantiate P<A, 0> with containing class=A and offset=0
void baz();
};
A a;
a.x.bar(); // results in a.baz()
Because the so defined inline class is incomplete and inherently a
template a function involving such a type by reference or pointer
(assuming (3) is allowed) in its signature becomes a template function.
*(5)* My answer here is no, there cannot be two inline subobjects of the
same type.
The problem:
class B {
P x, y;
void baz();
};
B b;
auto& x = b.x;
auto& y = b.y;
x.get();
y.get();
This poses a conflict because x and y have different offsets. The
compiler has to either store the offset inside b.x and b.y or generate
different types for x and y with different get/set implementations. The
latter results in references to B::x and B::y to be incompatible, which
is surprising because from the code they seem to have the same.
My proposed solution is to introduce a new type for each inline class if
you try to re-use some shared inline type.
class C {
inline class X : public P { } x; // Instantiate P<C, 0>
inline class Y : public P { } y; // Instantiate P<C, 4>
inline class Z {
// ...
} z1, z2; // ill-formed: only one instance per type
void baz();
};
C c;
auto& x = c.x;
auto& y = c.y;
x.get();
y.get();
Now there is no longer an ambiguity and it is clearly evident from the
fact that X and Y are distinct types in the code. If someone cames up
with a terser syntax that doesn't require repeating the inheritence
chain in every place, that would be awesome.
class D {
inline class : public P { } x, y; // ill-formed: only one inline
subobject per type
inline class : public P { } x*; // ill-formed
inline class : public P { } x[5]; // probably ill-formed because
(&x[0] - this) != (&x[1] - this)
};
*(3)* If I were to ignore the possibility of properties then I'd be in
favor of obtaining references/pointers to inline classes. For outside
code duck typing makes them behave just like references to regular
subobjects. A problem still remains with the address equality of
multiple empty inline subobjects if they had zero size.
If viewed under the light of a possible implementation for properties
then it may become a leaking abstraction. However I believe there is as
much value in passing a property by-reference as is passing a regular
value by-reference. It allows the callee modifying access (whether that
is good or bad is left undecided). Assuming properties were implicitly
convertible to the underlying type (by whichever means) then a function
expecting the underlying type invokes the conversion, whereas argument
deduction results in the inline subobject backing the property. This can
cause ambiguity depending on whether the inline class represents a
property or not.
class A {
// x is not supposed to be a property
inline class X { ... } x;
// y is supposed to be a property
inline class Y { operator int(); Y& operator=(int); ... } y;
};
template<class T> void foo(T);
A a;
foo(a.x); // ill-formed, cannot copy inline class
foo(a.y); // also ill-formed but shouldn't be: the intention is to pass
a.y's value
A possible way of clearing this ambiguity is with a contextual(?) keyword
class A {
// x is not supposed to be a property
inline class X { ... } x;
// y is supposed to be a property
// "property int" replaces the "inline class..." boilerplate
property int y { /* default implementation get/set auto-generated
where not user-provided */ };
};
template<class T> void foo(T);
A a;
foo(a.x); // ill-formed, cannot copy inline class
foo(a.y); // instantiation ill-formed at first, but it's a property so
try again with the implicit int conversion
When it comes to references, then as already stated the property type is
expected to be implicitly convertible to/assignable from an instance of
its underlying type. So even if argument deduction were to prefer the
inline class's type, the property still behaves under duck typing as-if
it were the value. Concretely in the following code:
tempalte<class T>
void foo(T& prop) { prop = 5; }
It should be irrelevant whether T deduces to "int" or
"MyHealthProperty<Player, 32>".
But it is more difficult with pointers. Whereas you can assign an int&
to a MyHealthProperty<Player, 32>& you cannot assign a int* to a
MyHealthProperty<Player, 32>*. Therefore it makes a pretty big
difference whether one deduces int* or MyHealthProperty<Player, 32>*.
But taking the address of the result of a property's getter is only
possible if it doesn't return an rvalue so this remains an open problem.
Well that took longer than expected. And now I realize that this still
overlaps with the properties thread. I guess that won't change unless it
is decided that inline classes are not the right tool to implement them.
Sigh.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--------------020008080104070705020706
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<html>
<head>
<meta content=3D"text/html; charset=3Dutf-8" http-equiv=3D"Content-Type=
">
</head>
<body bgcolor=3D"#FFFFFF" text=3D"#000000">
Am 31.08.2015 um 20:53 schrieb Matthew Woehlke:<br>
<blockquote cite=3D"mid:ms27sa$4pr$1@ger.gmane.org" type=3D"cite"> I
didn't said they "take up no space /if they contain no members/"
(emphasis added). I said they take up no space, *period*. </blockquot=
e>
It was me who said that and I believe this is an unnecessary
limitation.<br>
<br>
Let me try to recap all that was being suggested or discussed so far
in this and the property thread and propose some solutions of my
own. This may not be an exhaustive list.<br>
<br>
<b>(1)</b> An inline subobject is only instantiable and copyable as
part of the containing object besides user defined conversion
functions. Rules for compiler-generated functions for the containing
class behave as-if the inline subobject was a regular subobject.
Rules for deriving properties like standard layout and triviality
behave as-if the inline subobject was a regular subobject.<br>
<br>
<b>(2)</b> Name lookup in the inline subobject behaves as-if the
inline subobject was derived from the containing class.<br>
<br>
<b>(3)</b> Should it be possible to obtain a pointer/reference to
the inline subobject itself?<br>
Pro:<br>
* It can be passed as reference/pointer around on its own and be
involved in type deduction as long as it is not attempted to make a
copy of it outside of its containing class type.<br>
Con:<br>
* If empty inline subobjects have no storage then multiple instances
can have the same address.<br>
* In the context of properties (under the assumption they are
implemented with this technique) it is potentially a leaking
abstraction. Other languages don't allow you to pass around the
actual property itself, only the result of their getter. But then,
these languages usually don't have the concept of references. It
might be an interesting tool to pass a property by-reference to a
function and have that function operate on the property instead of
only its value. But it poses the ambiguity of when to deduce the
property object and when the property's value.<br>
<br>
<small><b><big>(4)</big></b></small> Should empty inline subobjects
have storage?<br>
Pro: zero overhead<br>
Con: see (3)<br>
<br>
<b>(5)</b> Should separate inline subobjects be allowed to have the
same type?<br>
Pro: It's convenient.<br>
Con:<br>
* If they aren't empty and access their state in member functions
the compiler must find a way to distinguish them. Either by storing
a hidden offset or by instantiating different actual types. Both
options aren't zero overhead either in object size or code size.<br>
* If every inline subobject has a different type and (3) is resolved
as yes then it is possible to safely static_cast between the
containing instance and the inline subobject.<br>
<br>
<b>(6)</b> It should be possible to declare re-usable inline classes
outside of the containing class. This means these inline classes
defined at namespace scope must be based on template instantiations
as they are only complete types when embedded in a containing class
and final name lookup and offset-patching can take place.<br>
<br>
Here are my attempts at solving some of the above problems.<br>
Keep in mind that this is largely pseudocode so please don't get
riled up on the syntax again.<br>
<br>
<b>(6)</b> I'll give this a try first because it is not dependent on
others:<br>
<br>
inline class P {<br>
=C2=A0 int i;<br>
public:<br>
=C2=A0 void set(int j) { i =3D j; }<br>
=C2=A0 int get() { return i; }<br>
=C2=A0 void bar() { this->baz(); }<br>
};<br>
In a definition like the above "this" is always dependent because
its type is unknown until the inline class is embedded in an actual
containing class. This is the same name lookup behavior as in
classes with a dependent base class, making it consistent with
existing practice and the suggested name lookup for inline
subobjects. It is basically syntax for compiler-generated CRTP
(which is a useful pattern on its own and this might actually make
it more accessible).<br>
It is then used like<br>
class A {<br>
=C2=A0 P x; // Instantiate P<A, 0> with containing class=3DA and
offset=3D0<br>
=C2=A0 void baz();<br>
};<br>
A a;<br>
a.x.bar(); // results in a.baz()<br>
<br>
Because the so defined inline class is incomplete and inherently a
template a function involving such a type by reference or pointer
(assuming (3) is allowed) in its signature becomes a template
function.<br>
<br>
<b>(5)</b> My answer here is no, there cannot be two inline
subobjects of the same type.<br>
<br>
The problem:<br>
class B {<br>
=C2=A0 P x, y;<br>
=C2=A0 void baz();<br>
};<br>
B b;<br>
auto& x =3D b.x;<br>
auto& y =3D b.y;<br>
x.get();<br>
y.get();<br>
This poses a conflict because x and y have different offsets. The
compiler has to either store the offset inside b.x and b.y or
generate different types for x and y with different get/set
implementations. The latter results in references to B::x and B::y
to be incompatible, which is surprising because from the code they
seem to have the same.<br>
<br>
My proposed solution is to introduce a new type for each inline
class if you try to re-use some shared inline type.<br>
class C {<br>
=C2=A0 inline class X : public P { } x; // Instantiate P<C, 0><br=
>
=C2=A0 inline class Y : public P { } y; // Instantiate P<C, 4><br=
>
=C2=A0 inline class Z {<br>
=C2=A0=C2=A0=C2=A0 // ...<br>
=C2=A0 } z1, z2; // ill-formed: only one instance per type<br>
=C2=A0 void baz();<br>
};<br>
C c;<br>
auto& x =3D c.x;<br>
auto& y =3D c.y;<br>
x.get();<br>
y.get();<br>
Now there is no longer an ambiguity and it is clearly evident from
the fact that X and Y are distinct types in the code. If someone
cames up with a terser syntax that doesn't require repeating the
inheritence chain in every place, that would be awesome.<br>
<br>
class D {<br>
=C2=A0 inline class : public P { } x, y; // ill-formed: only one inline
subobject per type<br>
=C2=A0 inline class : public P { } x*; // ill-formed<br>
=C2=A0 inline class : public P { } x[5]; // probably ill-formed because
(&x[0] - this) !=3D (&x[1] - this)<br>
};<br>
<br>
<b>(3)</b> If I were to ignore the possibility of properties then
I'd be in favor of obtaining references/pointers to inline classes.
For outside code duck typing makes them behave just like references
to regular subobjects. A problem still remains with the address
equality of multiple empty inline subobjects if they had zero size.<br>
<br>
If viewed under the light of a possible implementation for
properties then it may become a leaking abstraction. However I
believe there is as much value in passing a property by-reference as
is passing a regular value by-reference. It allows the callee
modifying access (whether that is good or bad is left undecided).
Assuming properties were implicitly convertible to the underlying
type (by whichever means) then a function expecting the underlying
type invokes the conversion, whereas argument deduction results in
the inline subobject backing the property. This can cause ambiguity
depending on whether the inline class represents a property or not.<br>
<br>
class A {<br>
=C2=A0 // x is not supposed to be a property<br>
=C2=A0 inline class X { ... } x;<br>
=C2=A0 // y is supposed to be a property<br>
=C2=A0 inline class Y { operator int(); Y& operator=3D(int); ... } =
y;<br>
};<br>
template<class T> void foo(T);<br>
A a;<br>
foo(a.x); // ill-formed, cannot copy inline class<br>
foo(a.y); // also ill-formed but shouldn't be: the intention is to
pass a.y's value<br>
<br>
A possible way of clearing this ambiguity is with a contextual(?)
keyword<br>
class A {<br>
=C2=A0 // x is not supposed to be a property<br>
=C2=A0 inline class X { ... } x;<br>
=C2=A0 // y is supposed to be a property<br>
=C2=A0 // "property int" replaces the "inline class..." boilerplate<br>
=C2=A0 property int y { /* default implementation get/set auto-generate=
d
where not user-provided */ };<br>
};<br>
template<class T> void foo(T);<br>
A a;<br>
foo(a.x); // ill-formed, cannot copy inline class<br>
foo(a.y); // instantiation ill-formed at first, but it's a property
so try again with the implicit int conversion<br>
<br>
When it comes to references, then as already stated the property
type is expected to be implicitly convertible to/assignable from an
instance of its underlying type. So even if argument deduction were
to prefer the inline class's type, the property still behaves under
duck typing as-if it were the value. Concretely in the following
code:<br>
<br>
tempalte<class T><br>
void foo(T& prop) { prop =3D 5; }<br>
<br>
It should be irrelevant whether T deduces to "int" or
"MyHealthProperty<Player, 32>".<br>
<br>
But it is more difficult with pointers. Whereas you can assign an
int& to a MyHealthProperty<Player, 32>& you cannot
assign a int* to a MyHealthProperty<Player, 32>*. Therefore it
makes a pretty big difference whether one deduces int* or
MyHealthProperty<Player, 32>*. But taking the address of the
result of a property's getter is only possible if it doesn't return
an rvalue so this remains an open problem.<br>
<br>
<br>
<br>
Well that took longer than expected. And now I realize that this
still overlaps with the properties thread. I guess that won't change
unless it is decided that inline classes are not the right tool to
implement them. Sigh.<br>
<br>
</body>
</html>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--------------020008080104070705020706--
.
Author: Jeremy Maitin-Shepard <jeremy@jeremyms.com>
Date: Mon, 31 Aug 2015 21:05:52 -0700 (PDT)
Raw View
------=_Part_63_1970838743.1441080353004
Content-Type: multipart/alternative;
boundary="----=_Part_64_700485809.1441080353004"
------=_Part_64_700485809.1441080353004
Content-Type: text/plain; charset=UTF-8
It seems the discussion here is touching on a bunch of a features that
would be potentially very useful:
1. Zero-size class data members.
2. Java-style inner classes: this could be done by allowing the definition
of classes with lambda-style capture specifications. It could be allowed
as a special case that if only a single reference or non-mutable pointer is
captured and there are no data members, then the extra level of indirection
is skipped, such that a pointer to the inner class is exactly the captured
pointer. Maybe this same optimization could also be supported in other
cases.
3. Expression aliases, so that a property proxy object can be accessed
using x.property rather than x.property().
In my view combining (2) and (3) to get properties is much cleaner, and
brings us a lot more generally useful functionality, than the inline class
idea. (1) wouldn't be needed for properties but would certainly save a lot
of metaprogramming hassle generally.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_64_700485809.1441080353004
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">It seems the discussion here is touching on a bunch of a f=
eatures that would be potentially very useful:<br><br>1. Zero-size class da=
ta members.<br><br>2. Java-style inner classes: this could be done by allow=
ing the definition of classes with lambda-style capture specifications.=C2=
=A0 It could be allowed as a special case that if only a single reference o=
r non-mutable pointer is captured and there are no data members, then the e=
xtra level of indirection is skipped, such that a pointer to the inner clas=
s is exactly the captured pointer.=C2=A0 Maybe this same optimization could=
also be supported in other cases.<br><br>3. Expression aliases, so that a =
property proxy object can be accessed using x.property rather than x.proper=
ty().<br><br>In my view combining (2) and (3) to get properties is much cle=
aner, and brings us a lot more generally useful functionality, than the inl=
ine class idea.=C2=A0 (1) wouldn't be needed for properties but would c=
ertainly save a lot of metaprogramming hassle generally.<br><br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_64_700485809.1441080353004--
------=_Part_63_1970838743.1441080353004--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Tue, 01 Sep 2015 09:42:28 -0400
Raw View
On 2015-08-31 18:21, Thiago Macieira wrote:
> On Monday 31 August 2015 17:21:23 Matthew Woehlke wrote:
>>> decltype(*this) &operator=(int newValue)
>>> { this->m_value = value; }
>>
>> -Werror=return-type! You forgot 'return *this;' ;-). (Which I mention
>> mainly because I would further hope that you can just write the return
>> type as 'auto' and let it be deduced from the 'return *this;'.)
>
> But if this is Outer*, return *this would be Outer&, not Outer::Inner&.
It's a little odd, because decltype(this) is Outer::Inner*, but you can
access the members of Outer through it. (Well... Inner can. Presumably
they are protected in that context, however. IOW, what you said in your
next message :-).)
>>> Questions:
>>> * can you actually obtain a reference to the property? Note that
>>> decltype(*this): does that expand to Outer or to the inner type?
>>
>> Yes, you can obtain a reference to the property / inline struct. The
>> type is Outer::Inner&, but the memory address is the same as the Outer&.
>>
>>> * if it's to the inner type, then you can bind a reference to it:
>>> decltype(auto) v = (obj.value = 1);
>>>
>>> and form a pointer even though operator& was disallowed:
>>> auto ptr = std::addressof(v);
>>
>> Er... where/why was operator& disallowed?
>
> Because I added:
> void *operator &() const = delete;
Ah, didn't see that. Nevertheless, I will repeat the question: *why* did
you do that? Do you wish to prevent taking the address of this specific
type?
(In general I don't think you can prevent std::addressof.)
>> I don't recall doing so
>> (though I admit my recollection may be faulty). It seems that one should
>> be allowed to do so; the type of the pointer is Outer::Inner*, with bits
>> equal to the Outer*.
>
> Another question:
> * How do I call a function in Outer that has the same name as one in the
> inner struct? Suggestion:
>
> void set(int newValue)
> { Outer::set(newValue); }
That's how I'd do it, yes.
> In other words, the inner, inline class behaves as if it derived from the
> outer class.
I think yes. I'm not sure I would have phrased it exactly that way
(maybe I will, now that you mention it), but I believe that, yes, the
effect is the same.
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Tue, 01 Sep 2015 10:56:59 -0400
Raw View
On 2015-08-31 21:02, Miro Knejp wrote:
> *(3)* Should it be possible to obtain a pointer/reference to the inline
> subobject itself?
>
> Con:
> * If empty inline subobjects have no storage then multiple instances can
> have the same address.
This is one reason to look at them not as subobjects but as derived
types. Consider:
class Foo { ... };
class Bar : protected Foo { ... };
I can have a Foo* and a Bar* pointing to the same actual object, which
are identical. This is very similar to the case of inline classes,
except that I can have many inline classes that are siblings rather than
an inheritance chain. In both cases, all have the same address.
> * In the context of properties (under the assumption they are
> implemented with this technique) it is potentially a leaking
> abstraction. Other languages don't allow you to pass around the actual
> property itself, only the result of their getter. But then, these
> languages usually don't have the concept of references. It might be an
> interesting tool to pass a property by-reference to a function and have
> that function operate on the property instead of only its value. But it
> poses the ambiguity of when to deduce the property object and when the
> property's value.
I think the loophole that needs to be closed here is ensuring that
trying to take a property by value calls a conversion operator. Taking a
property by reference should be transparently equivalent to taking its
value, except that the call to the getter gets shifted. (And potentially
called more than once.)
Come to think of it, this (lvalue conversion) may be a general problem
not limited to inline class properties.
> *(4)* Should empty inline subobjects have storage?
Yes, as that's one of the purposes of the feature.
> *(5)* Should separate inline subobjects be allowed to have the same type?
> Pro: It's convenient.
> Con:
> * If they aren't empty and access their state in member functions the
> compiler must find a way to distinguish them.
....which is why I wouldn't permit non-empty inline classes. Basically,
as soon as you allow them to be non-empty, it's hard to see how you can
retain any benefit from them being inline vs. degenerating into plain
old concrete classes.
> * If every inline subobject has a different type and (3) is resolved as
> yes then it is possible to safely static_cast between the containing
> instance and the inline subobject.
If every object has a different type, I don't see the benefit to them
having (non-static data) members in the first place. Just move the
members you want to put in them to the containing concrete class.
> *(6)* It should be possible to declare re-usable inline classes outside
> of the containing class. This means these inline classes defined at
> namespace scope must be based on template instantiations as they are
> only complete types when embedded in a containing class and final name
> lookup and offset-patching can take place.
They don't need to be templated. They just act like namespaces.
> inline class P {
> int i;
> public:
> void set(int j) { i = j; }
> int get() { return i; }
> void bar() { this->baz(); }
> };
Assuming that this is the complete context (which I believe was your
intent), this code will not compile, because P has no member 'baz'.
There is no delayed look-up. If you want to use this as a base class
e.g. for a property, use CRTP and reinterpret_cast 'this' to the derived
type.
Given a freestanding inline class, the only things you can access with
'this' are member functions of that class and its base inline classes
(if any), and static members of the same.
> It is basically syntax for compiler-generated CRTP (which is a useful
> pattern on its own and this might actually make it more accessible).
....which is something I'd like to see, but orthogonally. If we make CRTP
better, we shouldn't limit the improvement to inline classes.
> *(5)* My answer here is no, there cannot be two inline subobjects of the
> same type.
>
> The problem:
> class B {
> P x, y;
> void baz();
> };
> B b;
> auto& x = b.x;
> auto& y = b.y;
> x.get();
> y.get();
> This poses a conflict because x and y have different offsets. The
> compiler has to either store the offset inside b.x and b.y or generate
> different types for x and y with different get/set implementations. The
> latter results in references to B::x and B::y to be incompatible, which
> is surprising because from the code they seem to have the same.
>
> My proposed solution is to introduce a new type for each inline class if
> you try to re-use some shared inline type.
> class C {
> inline class X : public P { } x; // Instantiate P<C, 0>
> inline class Y : public P { } y; // Instantiate P<C, 4>
> inline class Z {
> // ...
> } z1, z2; // ill-formed: only one instance per type
> void baz();
> };
I don't see why the first should work but the second should be illegal.
That said, I think this could work, but I'm nervous about the amount of
"magic" involved. Maybe it would be better to try to get inline classes
in as storage-free and later improve them to permit storage?
> inline class : public P { } x*; // ill-formed
I'm not sure I agree (assuming you meant '*x'). Pointers to inline
classes are legal, so why not allow a class member that is such a pointer?
class Foo
{
public:
inline class : public P {} *x = _x;
private:
decltype(*x) _x;
}
I don't know *why* you'd ever do this, but I don't see a problem.
> inline class : public P { } x[5]; // probably ill-formed because
> (&x[0] - this) != (&x[1] - this)
This should fall under the same rule as 'P x, y'; see comments above.
(Pretty useless with storage-free inline classes, but...)
> If viewed under the light of a possible implementation for properties
> then it may become a leaking abstraction. However I believe there is as
> much value in passing a property by-reference as is passing a regular
> value by-reference. It allows the callee modifying access (whether that
> is good or bad is left undecided).
Assuming of course that the reference is non-const, in which case one
MUST pass the property reference anyway.
Repeating myself a bit here, but I see two possibilities: either the
callee wants a mutable reference, and so the only way to make the call
is to pass a reference to the property anyway, or else it takes a const
reference and passing the property reference could anyway only change
the program semantics if the callee uses the value multiple times, and
the value changes in the mean time.
> Assuming properties were implicitly convertible to the underlying
> type (by whichever means)
....which is sort of the point of why I defined the getter as a
conversion operator :-). (Notice I never made it explicit.)
> argument deduction results in the inline subobject backing the
> property.
Probably we would want to develop techniques to trigger conversion of a
property-like inline class as a deduced type. If derived from
std::property, this shouldn't be difficult. We almost certainly want
also a type trait to identify inline classes. The trick would be
automagically identifying the type of a single conversion operator.
(Including a utility class for this purpose in the proposal probably
wouldn't be amiss...)
> class A {
> // x is not supposed to be a property
> inline class X { ... } x;
> // y is supposed to be a property
> inline class Y { operator int(); Y& operator=(int); ... } y;
> };
> template<class T> void foo(T);
> A a;
> foo(a.x); // ill-formed, cannot copy inline class
> foo(a.y); // also ill-formed but shouldn't be: the intention is to pass
> a.y's value
I'm inclined to believe that attempting to copy/assign an inline class
in a deduced context should attempt to invoke a conversion operator.
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Tue, 01 Sep 2015 10:59:58 -0400
Raw View
On 2015-09-01 00:05, Jeremy Maitin-Shepard wrote:
> It seems the discussion here is touching on a bunch of a features that
> would be potentially very useful:
>
> 1. Zero-size class data members.
>
> 2. Java-style inner classes: this could be done by allowing the definition
> of classes with lambda-style capture specifications. It could be allowed
> as a special case that if only a single reference or non-mutable pointer is
> captured and there are no data members, then the extra level of indirection
> is skipped, such that a pointer to the inner class is exactly the captured
> pointer. Maybe this same optimization could also be supported in other
> cases.
>
> 3. Expression aliases, so that a property proxy object can be accessed
> using x.property rather than x.property().
>
> In my view combining (2) and (3) to get properties is much cleaner, and
> brings us a lot more generally useful functionality, than the inline class
> idea. (1) wouldn't be needed for properties but would certainly save a lot
> of metaprogramming hassle generally.
How is (3) relevant? If you implement properties like (2), then I assume
you are still implementing them something like an inner class with
get/set methods to represent the property. In which case, wouldn't that
class just be a member?
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Miro Knejp <miro.knejp@gmail.com>
Date: Tue, 1 Sep 2015 18:59:52 +0200
Raw View
> On 01 Sep 2015, at 16:56 , Matthew Woehlke <mwoehlke.floss@gmail.com> wro=
te:
>=20
>> inline class P {
>> int i;
>> public:
>> void set(int j) { i =3D j; }
>> int get() { return i; }
>> void bar() { this->baz(); }
>> };
>=20
> Assuming that this is the complete context (which I believe was your
> intent), this code will not compile, because P has no member 'baz'.
> There is no delayed look-up. If you want to use this as a base class
> e.g. for a property, use CRTP and reinterpret_cast 'this' to the derived
> type.
You know, it helps reading the entire section before responding. Please don=
=E2=80=99t just blindly comment on a snippet of code without considering th=
e explanation:
"In a definition like the above "this" is always dependent because its type=
is unknown until the inline class is embedded in an actual containing clas=
s. This is the same name lookup behavior as in classes with a dependent bas=
e class, making it consistent with existing practice ...=E2=80=9D
There *is* delayed lookup because I said so. It=E2=80=99s part of the solut=
ion. =E2=80=9Cthis" is dependent in the the above definition because I say =
so, therefore "this->baz()" lookup is delayed until instantiation. The comp=
iler has to internally generate some CRTP-like template construct to make i=
t work.
>=20
>> If viewed under the light of a possible implementation for properties
>> then it may become a leaking abstraction. However I believe there is as
>> much value in passing a property by-reference as is passing a regular
>> value by-reference. It allows the callee modifying access (whether that
>> is good or bad is left undecided).
>=20
> Assuming of course that the reference is non-const, in which case one
> MUST pass the property reference anyway.
Unless the property converts to an lvalue-reference.
>=20
> Repeating myself a bit here, but I see two possibilities: either the
> callee wants a mutable reference, and so the only way to make the call
> is to pass a reference to the property anyway, or else it takes a const
> reference and passing the property reference could anyway only change
> the program semantics if the callee uses the value multiple times, and
> the value changes in the mean time.
You have the same problems with regular references already. The value of a =
reference you hold could change between accesses.
>=20
> I'm inclined to believe that attempting to copy/assign an inline class
> in a deduced context should attempt to invoke a conversion operator.
This is some very special behavior and I think it should be reserved for th=
ings explicitly marked as properties. Otherwise you cannot reliably SFINAE =
on inline classes that don=E2=80=99t represent properties.
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr>
Date: Tue, 1 Sep 2015 19:27:06 +0200
Raw View
This is a multi-part message in MIME format.
--------------090705000504020803020501
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: quoted-printable
Le 31/08/15 23:01, Thiago Macieira a =C3=A9crit :
> On Monday 31 August 2015 14:53:56 Matthew Woehlke wrote:
>> I didn't said they "take up no space /if they contain no members/"
>> (emphasis added). I said they take up no space, *period*.
> The problem with taking up no space is that you can't have two of them in=
a
> class, because their (empty) subobjects would have the same address. You
> simply can't have that, period.
I believe that inline classes can be useful independently of whether its=20
size if 0 when there is no data. While this could have its importance=20
for properties without storage, it seems less important to me when using=20
inline classes as services or subjects of an object as them use to carry=20
some data.
I propose to let this point out of the current discussion and see what=20
could be done even if the size is not empty.
> It sounds to me that the actual class would just be an "adaptor" or "view=
" of
> the outer class.
Yes this is the use case I have in mind. I use to call them services or=20
subjects as they are always related to a given object.
IMO we should be able to take the address of an inline class instance, I=20
see no problem with that.
> For example, an implementation could make it so the object
> carries the "this" pointer to the outer. Something like:
>
> class Outer
> {
> int m_value;
> public:
> // syntax potentially conflicts with inline variables
> inline struct {
> decltype(*this) &operator=3D(int newValue)
> { this->m_value =3D value; }
> operator int() const { return this->m_value; }
> void *operator &() const =3D delete;
> } value;
>
> /* if the syntax is a problem, we can use:
> auto value =3D inline struct { ... };
> */
> };
I would suggest to use this to refer to the inline class instance and=20
this->class when referring to the outer class instance.
As an inline class instance exists only on the scope of an outer class=20
the compiler could always know about the address of this->class.
Your example could be rewritten as
inline struct {
decltype(*this) &operator=3D(int newValue)
{ this->*class.*m_value =3D value; return *this; }
operator int() const { return this->*class.*m_value; }
} value;
> Where the *real* inline struct would be:
>
> struct _RealInline { Outer *this; };
>
> But if I were implementing this, I would make the ABI avoid the double
> indirection and not store a pointer to the this pointer, but the pointer
> directly, as you would an Outer *.
I would not use a pointer as each inline class instance could have an=20
implicit offset respect to its outer class instance.
> Questions:
> * can you actually obtain a reference to the property? Note that
> decltype(*this): does that expand to Outer or to the inner type?
I think we should be able. It seems to me that making the difference=20
between this and this->class inside a inline class would allow it.
decltype(*this) is the inner inline class
decltype(*this->class) is the outer class
>
> * if it's to the outer type, then this could be surprising:
> (obj.value =3D 1) =3D 2;
>
> * if it's to the inner type, then you can bind a reference to it:
> decltype(auto) v =3D (obj.value =3D 1);
> and form a pointer even though operator& was disallowed:
> auto ptr =3D std::addressof(v);
This is something that you can already do with any class that disables=20
the operator&, isn't it?
Note that I'm not for disabling it the operator& implicitly for inline=20
classes.
> * a third option is for operator=3D to return a prvalue (int) or void. =
Note
> that decltype(*this) in that context is currently not allowed, so you
> couldn't get the type of an anonymous struct that easily (but not
> impossible).
>
If we make abstraction of the empty issue, the inline class I have in=20
mind could be emulated as follows
struct A
{
template <int OFFSET>
friend struct X : inline_class<A, OFFSET>
{
void f() { this_class().x =3D0; }
...
int val;
};
X<0> prop1; //note that the offsets should be calculated by the compiler
X<sizeof<int>> prop2;
int x;
};
inline_class<A, OFFSET> defines the function this_class depending on the=20
OFFSET parameter.
More is needed to allow assigning from two instances of the same inline=20
class. I let this an exercise.
Note that this copy can be trivial when the members are the inline class=20
instance doesn't stores a pointer to the outer class.
This emulation is off course error prone as the user must replace the=20
compiler when giving the valid offsets.
Vicente
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
--------------090705000504020803020501
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<html>
<head>
<meta content=3D"text/html; charset=3Dutf-8" http-equiv=3D"Content-Type=
">
</head>
<body text=3D"#000000" bgcolor=3D"#FFFFFF">
<div class=3D"moz-cite-prefix"><br>
Le 31/08/15 23:01, Thiago Macieira a =C3=A9crit=C2=A0:<br>
</div>
<blockquote cite=3D"mid:3004775.sWvc9oTlT2@tjmaciei-mobl4" type=3D"cite=
">
<pre wrap=3D"">On Monday 31 August 2015 14:53:56 Matthew Woehlke wrot=
e:
</pre>
<blockquote type=3D"cite">
<pre wrap=3D"">I didn't said they "take up no space /if they contai=
n no members/"
(emphasis added). I said they take up no space, *period*.
</pre>
</blockquote>
<pre wrap=3D"">
The problem with taking up no space is that you can't have two of them in a=
=20
class, because their (empty) subobjects would have the same address. You=20
simply can't have that, period.
</pre>
</blockquote>
I believe that inline classes can be useful independently of whether
its size if 0 when there is no data. While this could have its
importance for properties without storage, it seems less important
to me when using inline classes as services or subjects of an object
as them use to carry some data.<br>
I propose to let this point out of the current discussion and see
what could be done even if the size is not empty.<br>
<br>
<br>
<blockquote cite=3D"mid:3004775.sWvc9oTlT2@tjmaciei-mobl4" type=3D"cite=
">
<pre wrap=3D"">
It sounds to me that the actual class would just be an "adaptor" or "view" =
of=20
the outer class. </pre>
</blockquote>
Yes this is the use case I have in mind. I use to call them services
or subjects as they are always related to a given object.<br>
IMO we should be able to take the address of an inline class
instance, I see no problem with that.<br>
<blockquote cite=3D"mid:3004775.sWvc9oTlT2@tjmaciei-mobl4" type=3D"cite=
">
<pre wrap=3D"">For example, an implementation could make it so the ob=
ject=20
carries the "this" pointer to the outer. Something like:
class Outer
{
int m_value;
public:
// syntax potentially conflicts with inline variables
inline struct {
decltype(*this) &operator=3D(int newValue)
{ this->m_value =3D value; }
operator int() const { return this->m_value; }
void *operator &() const =3D delete;
} value;
/* if the syntax is a problem, we can use:
auto value =3D inline struct { ... };
*/
};
</pre>
</blockquote>
I would suggest to use this to refer to the inline class instance
and this->class when referring to the outer class instance.<br>
As an inline class instance exists only on the scope of an outer
class the compiler could always know about the address of
this->class. <br>
Your example could be rewritten as<br>
<pre wrap=3D""> inline struct {
decltype(*this) &operator=3D(int newValue)
{ this-><b>class.</b>m_value =3D value; return *this; }
operator int() const { return this-><b>class.</b>m_value; }
} value;
</pre>
<br>
<br>
<blockquote cite=3D"mid:3004775.sWvc9oTlT2@tjmaciei-mobl4" type=3D"cite=
">
<pre wrap=3D"">
Where the *real* inline struct would be:
struct _RealInline { Outer *this; };
But if I were implementing this, I would make the ABI avoid the double=20
indirection and not store a pointer to the this pointer, but the pointer=20
directly, as you would an Outer *.
</pre>
</blockquote>
I would not use a pointer as each inline class instance could have
an implicit offset respect to its outer class instance.<br>
<blockquote cite=3D"mid:3004775.sWvc9oTlT2@tjmaciei-mobl4" type=3D"cite=
">
</blockquote>
<blockquote cite=3D"mid:3004775.sWvc9oTlT2@tjmaciei-mobl4" type=3D"cite=
">
<pre wrap=3D"">
Questions:
* can you actually obtain a reference to the property? Note that=20
decltype(*this): does that expand to Outer or to the inner type?</pre>
</blockquote>
I think we should be able. It seems to me that making the difference
between this and this->class inside a inline class would allow
it.<br>
<br>
decltype(*this) is the inner inline class<br>
decltype(*this->class) is the outer class<br>
<br>
<blockquote cite=3D"mid:3004775.sWvc9oTlT2@tjmaciei-mobl4" type=3D"cite=
">
<pre wrap=3D"">
* if it's to the outer type, then this could be surprising:
(obj.value =3D 1) =3D 2;
* if it's to the inner type, then you can bind a reference to it:
decltype(auto) v =3D (obj.value =3D 1);
and form a pointer even though operator& was disallowed:
auto ptr =3D std::addressof(v);
</pre>
</blockquote>
This is something that you can already do with any class that
disables the operator&, isn't it?<br>
Note that I'm not for disabling it the operator& implicitly for
inline classes.<br>
<blockquote cite=3D"mid:3004775.sWvc9oTlT2@tjmaciei-mobl4" type=3D"cite=
">
<pre wrap=3D"">
* a third option is for operator=3D to return a prvalue (int) or void. Not=
e=20
that decltype(*this) in that context is currently not allowed, so you=20
couldn't get the type of an anonymous struct that easily (but not=20
impossible).
</pre>
</blockquote>
If we make abstraction of the empty issue, the inline class I have
in mind could be emulated as follows<br>
<br>
struct A<br>
{<br>
=C2=A0 template <int OFFSET> <br>
=C2=A0 friend struct X : inline_class<A, OFFSET><br>
=C2=A0 {<br>
=C2=A0=C2=A0=C2=A0 void f() { this_class().x =3D0; }<br>
=C2=A0=C2=A0=C2=A0 ...<br>
=C2=A0=C2=A0=C2=A0 int val;<br>
=C2=A0 };<br>
=C2=A0 X<0> prop1; //note that the offsets should be calculated b=
y
the compiler<br>
=C2=A0 X<sizeof<int>> prop2;<br>
=C2=A0 int x;<br>
};<br>
<br>
inline_class<A, OFFSET> defines the function this_class
depending on the OFFSET parameter. <br>
<br>
More is needed to allow assigning from two instances of the same
inline class. I let this an exercise.<br>
Note that this copy can be trivial when the members are the inline
class instance doesn't stores a pointer to the outer class. =C2=A0 <br>
<br>
This emulation is off course error prone as the user must replace
the compiler when giving the valid offsets.<br>
<br>
Vicente<br>
<br>
<br>
</body>
</html>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--------------090705000504020803020501--
.
Author: "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr>
Date: Tue, 1 Sep 2015 19:34:25 +0200
Raw View
Le 01/09/15 00:21, Thiago Macieira a =C3=A9crit :
> On Monday 31 August 2015 17:21:23 Matthew Woehlke wrote:
>>> =09
>
> Another question:
> * How do I call a function in Outer that has the same name as one in th=
e
> inner struct? Suggestion:
>
> void set(int newValue)
> { Outer::set(newValue); }
>
> In other words, the inner, inline class behaves as if it derived from the
> outer class.
>
Or
void set(int newValue)
{ this->class.set(newValue); }
if we adopt this->class to refer to the outer class.
Vicente
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Tue, 01 Sep 2015 13:45:14 -0400
Raw View
On 2015-09-01 12:59, Miro Knejp wrote:
>> On 01 Sep 2015, at 16:56 , Matthew Woehlke wrote:
>>> inline class P {
>>> int i;
>>> public:
>>> void set(int j) { i =3D j; }
>>> int get() { return i; }
>>> void bar() { this->baz(); }
>>> };
>>
>> Assuming that this is the complete context (which I believe was your
>> intent), this code will not compile, because P has no member 'baz'.
>> There is no delayed look-up. If you want to use this as a base class
>> e.g. for a property, use CRTP and reinterpret_cast 'this' to the derived
>> type.
>=20
> You know, it helps reading the entire section before responding. Please d=
on=E2=80=99t just blindly comment on a snippet of code without considering =
the explanation:
> "In a definition like the above "this" is always dependent because its ty=
pe is unknown until the inline class is embedded in an actual containing cl=
ass. This is the same name lookup behavior as in classes with a dependent b=
ase class, making it consistent with existing practice ...=E2=80=9D
I did read the whole thing, and I stand by what I said (if not how I
said it).
Assuming that P is a freestanding inline class (that is, you did not
omit a concrete class which contains the definition of P), then the
example is (read: "IMO, should be") ill-formed, because P has no member
'baz'.
I disagree that this should magically become well-formed when you have a
member of type P inside some class that has a 'baz'. What I was trying
to say is that I... am not comfortable with your suggestion that the
above is magically a CRTP template. If you want CRTP-like behavior...
use CRTP.
That said, I would be wholly in favor of an *orthogonal* proposal to
make CRTP easier... but it should work for non-inline-class cases also,
and it should still require some sort of decoration.
>>> If viewed under the light of a possible implementation for properties
>>> then it may become a leaking abstraction. However I believe there is as
>>> much value in passing a property by-reference as is passing a regular
>>> value by-reference. It allows the callee modifying access (whether that
>>> is good or bad is left undecided).
>>
>> Assuming of course that the reference is non-const, in which case one
>> MUST pass the property reference anyway.
>
> Unless the property converts to an lvalue-reference.
Er... well, sure, except that I can't imagine how the property doing
that isn't broken :-).
Let me rephrase that: if the callee wants a mutable reference, then
almost certainly the callee *needs* to accept the property itself by
reference, not a reference to the underlying value. So, in that case,
you *want* the "leaky abstraction".
In any other case, either the conversion gets invoked anyway and there
is no "leak", or you aren't passing a mutable property anyway and there
is no semantic change and so the "leak" doesn't matter.
That's what I meant... that I don't see an actual problem here.
>> Repeating myself a bit here, but I see two possibilities: either the
>> callee wants a mutable reference, and so the only way to make the call
>> is to pass a reference to the property anyway, or else it takes a const
>> reference and passing the property reference could anyway only change
>> the program semantics if the callee uses the value multiple times, and
>> the value changes in the mean time.
>
> You have the same problems with regular references already. The value
> of a reference you hold could change between accesses.
True, but in the non-property case, a correctly designed API won't ever
give you a reference.
>> I'm inclined to believe that attempting to copy/assign an inline class
>> in a deduced context should attempt to invoke a conversion operator.
>
> This is some very special behavior and I think it should be reserved=20
> for things explicitly marked as properties. Otherwise you cannot=20
> reliably SFINAE on inline classes that don=E2=80=99t represent properties=
..
Understood. I admit this area is a little tricky, and is probably best
dealt with by providing a way for the programmer to explicitly indicate
that a class should be subject to such conversion.
If "properties" always derived from std::property, we could also go by
that, but I would prefer to not require that. (There would also be a
type trait to ask about an inline class type, but that's probably not
adequate on its own.)
I think my current plan is to punt on this for now.
--=20
Matthew
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Tue, 01 Sep 2015 13:52:23 -0400
Raw View
On 2015-09-01 13:34, Vicente J. Botet Escriba wrote:
> Le 01/09/15 00:21, Thiago Macieira a =C3=A9crit :
>> Another question:
>> * How do I call a function in Outer that has the same name as one in
>> the
>> inner struct? Suggestion:
>>
>> void set(int newValue)
>> { Outer::set(newValue); }
>>
>> In other words, the inner, inline class behaves as if it derived from th=
e
>> outer class.
>
> Or
>=20
> void set(int newValue)
> { this->class.set(newValue); }
>=20
> if we adopt this->class to refer to the outer class.
We *could* do that, but it means additional syntax change for minimal gain.
--=20
Matthew
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Tue, 01 Sep 2015 14:02:31 -0400
Raw View
This is a multi-part message in MIME format.
--------------060601070308080709070206
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On 2015-08-30 15:13, Klaim - Jo=C3=ABl Lamotte wrote:
> My suggestion was to discuss this outside the properties thread because
> it's impossible to follow with the several sub-discussions ongoing.
Also per Nicol's request, I'm attaching=C2=B9 my current draft. This isn't
exactly formal proposal yet (in particular, there is no standardese at
all), but I'm trying to at least start in the direction of a more formal
paper rather than just paragraphs in an e-mail.
The "zero storage object" rationale is... "lacking", since I'm less
familiar with that one. I'd appreciate help filling that out.
Comments encouraged, of course. Also, please remind me what bits I
forgot about, as I'm sure there are some.
(=C2=B9 I hope to get this on github eventually, but I don't have the abili=
ty
to access my account at the moment.)
Thanks,
--=20
Matthew
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
--------------060601070308080709070206
Content-Type: text/prs.fallenstein.rst;
name="inline-class.rst"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="inline-class.rst"
=2E. note::
For the sake of brevity, the term "class" is used throughout most of
this document. As always, the difference between a ``class`` and a
``struct`` is default access protection. Unless specifically stated
otherwise, all instances of either "struct" or "class" should be unde=
rstood
to mean "class or struct".
Overview
=3D=3D=3D=3D=3D=3D=3D=3D
This proposal introduces the concept of an "inline class". An inline clas=
s is
a special type which is not an object in the normal sense and has no stor=
age.
The use of inline classes is to provide a zero overhead "proxy" for an ob=
ject,
allowing access to all or part of the object using a modified interface.
Rationale
=3D=3D=3D=3D=3D=3D=3D=3D=3D
Users have repeatedly asked for properties to be added to C++. The genera=
l
consensus has been that these are too much of a niche feature to justify
expending precious syntax on a first class implementation. Additionally, =
a
"fully native" property support must make a trade-off between functionali=
ty
and simplicity, and makes limited reuse of existing language features. Wh=
ile a
purely library solution is possible, there are various issues that come u=
p in
such an approach, such as back references to the containing class, and ho=
w to
specify the get/set methods, that make such an implementation less than
optimal.
Besides properties, there is also a desire to be able to add zero storage=
members to types.
Lastly, the ability to access different "presentations" of a class is val=
uable
in some cases. A good example is a color class which internally stores on=
ly RGB
values, but offers interfaces to manipulate the color using a variety of =
color
spaces, especially where multiple such spaces have overlapping component =
names
(e.g. HSL, HSV, HCY). Inline classes provide a mechanism for retrieving a=
zero
overhead interface that allows the user to work with a class "as an X", w=
here
the underlying class supports multiple "X"s.
Description
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
An inline class provides a storage free interface (and is not itself an
"object" in the usual sense). As a result, an inline class may not have
non-static data members, virtual members, or virtual bases, but may have =
member
functions and static data members in the usual manner. Additionally, an i=
nline
class may have members that are themselves inline classes.
An inline class may not be directly constructed, destroyed, or copied (al=
though
an inline class may define a copy/move assignment operator), and in parti=
cular
has no constructors or destructor. Inline classes have no role in the
construction or destruction of concrete (i.e. non-inline) classes that co=
ntain
them, as they have no storage to be constructed, copied, or destroyed.
Similarly, the presence of inline class members in a concrete class does =
not
affect the layout of the Containing Concrete Class (CCC) in any manner.
While the typical use of an inline class is as part of a containing class=
, it
is also permitted to define Freestanding Inline Classes (FIC's), which ma=
y be
used generically inside of concrete classes and/or as base classes of Con=
tained
Inline Classes (CIC's; that is, inline classes that are members |--| whet=
her
directly or indirectly |--| of a concrete class). It is illegal to name o=
r take
the size (``sizeof``) of a FIC. The size of a CIC is equal to the size of=
its
CCC.
Contained Inline Classes may be named as types and stored or passed eithe=
r by
reference or by value. The address of a CIC is bitwise equal to the addre=
ss of
the CCC that contains it. The actual type name may be untypable, as in th=
e case
of anonymous inline classes, or the type name of an inline class member o=
f a
FIC type. The actual type of a FIC used as a member of a concrete class i=
s
unique to the CCC (conceptually, the compiler generates a new copy of the=
FIC
type for every unique CCC which contains a member of such type). Otherwis=
e, the
type name of a CIC is the same as a contained traditional (concrete) clas=
s. For
example:
inline class Free { ... };
class Foo
{
Free x;
inline class { ... } y;
inline class Bar { ... } z;
};
Foo foo;
In the above, the type name of ``foo.z`` is ``Foo::Bar``, while the type =
names
of ``foo.x`` and ``foo.y`` are untypable.
A CIC reference or pointer, including its ``this`` pointer, may be used t=
o
access the members of the containing class(es) as if the inline class was=
derived (using ``protected`` access) from its containing class. (In case =
of
nested inline classes, the behavior is as if the innermost inline class d=
erives
from its immediately containing class, which in turn derives from its own=
immediate containing class, and so on up to the CCC.) Member shadowing fo=
llows
the usual rules per the above "as if derived from" logic, and shadowed me=
mbers
may be accessed via qualification in like manner. A pointer to an inline =
class
may be ``static_cast``\ ed to a class type which derives from the type of=
the
original pointer.
As with concrete classes, inline classes may be templated. Additionally, =
a
concrete class may inherit from an inline class. Doing so causes the inli=
ne
base class to be treated as if it were a concrete class for the purpose o=
f
defining the derived class.
An inline class may be forward declared. An undefined (that is, declared
but not yet defined) inline class may additionally be used as a member of=
another class (inline or concrete); this is possible since the inline cla=
ss
member has no effect on the storage or layout of the containing class.
A type trait std::is_inline_type shall be introduced to detect if a typen=
ame is
an inline class.
Discussion
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
I have chosen the terminology "inline class" to indicate an entity that e=
xists
inside of a concrete class ("inline") without its own dedicated storage, =
which
otherwise in many ways resembles a class, and because this uses a sequenc=
e of
existing keywords which is not currently legal, thus avoiding any potenti=
al
change in behavior of existing code. This terminology is, however, contes=
ted,
and may not be optimal. Alternate syntax which provides the same function=
ality
would be acceptable, and may even be preferable; the author does not, how=
ever,
have an alternate syntax suggestion at this time.
Because a CIC's address equals the CCC address, it is possible for a conc=
rete
class to have several inline class members whose addresses are equal to t=
hat of
the concrete class instance. The author believes that this does not prese=
nt a
problem; inline classes are conceptually similar to downcasts, except tha=
t the
various types in this case would be siblings rather than links in an ance=
stral
chain. For similar reasons, the author does not expect that inline classe=
s
would create issues with type punning / aliasing.
Since an inline class is not copyable (by value), attempting to pass an i=
nline
class by value (most likely, this would be through a templated parameter =
type)
would result in an error. Since this is obviously undesirable for propert=
ies,
one solution is that inline classes specifically should attempt to invoke=
a
conversion operator in such case; where multiple conversion operators are=
present, the resulting program would naturally be ill-formed. This issue
however can occur also with non-inline classes, and so it may be desirabl=
e to
address the issue separately in a way that works for both inline and conc=
rete
classes to allow an implicit value conversion.
Future Direction
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
As will be illustrated in `Appendix I`_, enabling a full suite of arithme=
tic
operators for a property class requires a) that the user explicitly defin=
e
every operator to be supported, or else b) call upon a template base clas=
s
using CRTP\ [#]_. This requires that the inline class type be named rathe=
r than
anonymous. In general, the requirement in CRTP to reiterate the name of t=
he
derived class violates the DRY\ [#]_ principle, and as such, may be open =
to
improvement. Such a change should be targeted at all uses of CRTP, not ju=
st as
it pertains to inline classes, and therefore is most appropriately addres=
sed in
an orthogonal manner.
Various individuals have expressed a desire to support non-empty inline
classes. Since this introduces a number of additional issues and is only
possible in limited situations (or with additional "magic", such as impli=
cit
type replication), and since the author feels that such support provides =
only
limited value, the author proposes that any such support should be introd=
uced
as a follow-on proposal, rather than attempting to incorporate it into th=
e
initial proposal.
Acknowledgments
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
The author wishes to thank Nicol Bolas, Edward Catmur, Miro Knejp and Thi=
ago
Macieira for participating in the discussion and helping to hash out the =
many
issues that must be addressed, and also everyone who has persisted in ask=
ing
for properties in the face of steadfast resistance.
Appendix I
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
While one intended use case of this feature is to support property-like
constructs in a clean manner, a typical use case still requires the user =
to
overload every arithmetic operator. This is in fact not a new problem, bu=
t
exists for any user defined value-like types, and is already addressed by=
some
libraries (e.g. ``boost::less_than_comparable``). The syntax used to crea=
te
properties via inline classes is also different from existing practice an=
d may
be considered esoteric to novice users. To alleviate these issues, the
following library classes are proposed:
template <typename T, typename Base> inline struct arithmetic
{
Base& operator+=3D(T const& value)
{
auto& self =3D *(Base*)(this);
self =3D self + value;
return *self;
}
// ...similar for -=3D, *=3D, etc.
}
template <typename Prop> inline struct property
: arithmetic<decltype((Prop)(nullptr)->get()), Prop>
{
using Type =3D decltype((Prop)(nullptr)->get());
operator Type() const { return get(); }
Prop& operator=3D(Type const& value)
{
auto& self =3D *(Prop*)(this);
return self.set(value);
}
Type get() const
{
auto& self =3D *(Prop*)(this);
return self.get();
}
property<Prop>& set(Type const& value)
{
*this =3D value;
return this;
}
}
The provided definitions are meant to be illustrative, not normative. The=
names, likewise, are suggestions and are open to change. Additionally, th=
e use
of ``const&`` to accept values should be predicated on the efficiency of =
doing
so.
=2E. |--| unicode:: U+2014
=2E. [#] `Curiously Recurring Template Pattern <https://en.wikipedia.org/=
wiki/Curiously_recurring_template_pattern>`_
=2E. [#] `Don't Repeat Yourself <https://en.wikipedia.org/wiki/Don't_repe=
at_yourself>`_
--------------060601070308080709070206--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Tue, 01 Sep 2015 14:02:35 -0400
Raw View
On 2015-09-01 13:27, Vicente J. Botet Escriba wrote:
> I believe that inline classes can be useful independently of whether its
> size if 0 when there is no data. While this could have its importance
> for properties without storage, it seems less important to me when using
> inline classes as services or subjects of an object as them use to carry
> some data.
I fail to see the value of an "inline" class vs. a normal class if they
*don't* provide the zero-storage guarantee. At that point, all you've
done is avoid the back-reference pointer to the containing class. Which
you could do anyway with pointer arithmetic, which moreover should be
equally efficient as if it was a compiler feature.
Conversely, trying to maintain the utility of inline classes *without*
the zero-storage guarantee makes them significantly more complicated to
implement.
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Miro Knejp <miro.knejp@gmail.com>
Date: Tue, 1 Sep 2015 20:06:05 +0200
Raw View
> On 01 Sep 2015, at 19:45 , Matthew Woehlke <mwoehlke.floss@gmail.com> wro=
te:
>=20
> I disagree that this should magically become well-formed when you have a
> member of type P inside some class that has a 'baz'. What I was trying
> to say is that I... am not comfortable with your suggestion that the
> above is magically a CRTP template. If you want CRTP-like behavior...
> use CRTP.
If you have this->foo() in a class with a dependent base class it also magi=
cally becomes well-formed when instantiated. If we assume name lookup in in=
line classes works as-if they were derived from the parent, this is one con=
sequence of that. I don=E2=80=99t see the problem. The fact it can be reali=
zed by the compiler with a CRTP-like mechanism is an implementation detail.=
All the standard needs to say is that the type of =E2=80=9Cthis=E2=80=9D i=
s dependent until the inline class is added to a parent, at which point two=
-phase lookup is performed. Even if we omit the two-phase lookup thing, the=
compiler still has to do *some* CRTP magic for non-empty inline classes.
Even if only empty types are considered to begin with we can at least antic=
ipate non-empty inline classes in the future and not artificially restrict =
our future selves.
>=20
> True, but in the non-property case, a correctly designed API won't ever
> give you a reference.
A correctly designed API won=E2=80=99t do many things. If it=E2=80=99s not =
ill-formed it needs to be specified.
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr>
Date: Tue, 1 Sep 2015 20:15:55 +0200
Raw View
Le 01/09/15 03:02, Miro Knejp a =C3=A9crit :
> Am 31.08.2015 um 20:53 schrieb Matthew Woehlke:
>> I didn't said they "take up no space /if they contain no members/"=20
>> (emphasis added). I said they take up no space, *period*.=20
> It was me who said that and I believe this is an unnecessary limitation.
>
> Let me try to recap all that was being suggested or discussed so far=20
> in this and the property thread and propose some solutions of my own.=20
> This may not be an exhaustive list.
>
> *(1)* An inline subobject is only instantiable and copyable as part of=20
> the containing object besides user defined conversion functions. Rules=20
> for compiler-generated functions for the containing class behave as-if=20
> the inline subobject was a regular subobject. Rules for deriving=20
> properties like standard layout and triviality behave as-if the inline=20
> subobject was a regular subobject.
Agreed.
>
> *(2)* Name lookup in the inline subobject behaves as-if the inline=20
> subobject was derived from the containing class.
Frindship should work also.
>
> *(3)* Should it be possible to obtain a pointer/reference to the=20
> inline subobject itself?
yes.
> Pro:
> * It can be passed as reference/pointer around on its own and be=20
> involved in type deduction as long as it is not attempted to make a=20
> copy of it outside of its containing class type.
I don't see the trouble copying inline class instances between two outer=20
class instances if they are of the good type.
> Con:
> * If empty inline subobjects have no storage then multiple instances=20
> can have the same address.
I believe that this is orthogonal and related to the possibility of=20
having object of size 0, which should be taken by another proposal.
> * In the context of properties (under the assumption they are=20
> implemented with this technique) it is potentially a leaking=20
> abstraction. Other languages don't allow you to pass around the actual=20
> property itself, only the result of their getter. But then, these=20
> languages usually don't have the concept of references. It might be an=20
> interesting tool to pass a property by-reference to a function and=20
> have that function operate on the property instead of only its value.=20
> But it poses the ambiguity of when to deduce the property object and=20
> when the property's value.
I don't seethe problem. please could you clarify?
>
> *(4)* Should empty inline subobjects have storage?
Lets say no for the time being.
> Pro: zero overhead
> Con: see (3)
>
> *(5)* Should separate inline subobjects be allowed to have the same type?
yes.
> Pro: It's convenient.
> Con:
> * If they aren't empty and access their state in member functions the=20
> compiler must find a way to distinguish them. Either by storing a=20
> hidden offset or by instantiating different actual types. Both options=20
> aren't zero overhead either in object size or code size.
Right.
> * If every inline subobject has a different type and (3) is resolved=20
> as yes then it is possible to safely static_cast between the=20
> containing instance and the inline subobject.
>
I don't understand this cons.
> *(6)* It should be possible to declare re-usable inline classes=20
> outside of the containing class. This means these inline classes=20
> defined at namespace scope must be based on template instantiations as=20
> they are only complete types when embedded in a containing class and=20
> final name lookup and offset-patching can take place.
I would say this will be nice, but I could live without.
>
> Here are my attempts at solving some of the above problems.
> Keep in mind that this is largely pseudocode so please don't get riled=20
> up on the syntax again.
>
> *(6)* I'll give this a try first because it is not dependent on others:
>
> inline class P {
> int i;
> public:
> void set(int j) { i =3D j; }
> int get() { return i; }
> void bar() { this->baz(); }
> };
> In a definition like the above "this" is always dependent because its=20
> type is unknown until the inline class is embedded in an actual=20
> containing class. This is the same name lookup behavior as in classes=20
> with a dependent base class, making it consistent with existing=20
> practice and the suggested name lookup for inline subobjects. It is=20
> basically syntax for compiler-generated CRTP (which is a useful=20
> pattern on its own and this might actually make it more accessible).
> It is then used like
> class A {
> P x; // Instantiate P<A, 0> with containing class=3DA and offset=3D0
> void baz();
> };
> A a;
> a.x.bar(); // results in a.baz()
>
> Because the so defined inline class is incomplete and inherently a=20
> template a function involving such a type by reference or pointer=20
> (assuming (3) is allowed) in its signature becomes a template function.
Agreed.
>
>
> *(5)* My answer here is no, there cannot be two inline subobjects of=20
> the same type.
>
> The problem:
> class B {
> P x, y;
> void baz();
> };
> B b;
> auto& x =3D b.x;
> auto& y =3D b.y;
> x.get();
> y.get();
> This poses a conflict because x and y have different offsets. The=20
> compiler has to either store the offset inside b.x and b.y or generate=20
> different types for x and y with different get/set implementations.=20
> The latter results in references to B::x and B::y to be incompatible,=20
> which is surprising because from the code they seem to have the same.
You are right that they are incompatible in the current standard if=20
different types are generated. We need to find a specific solution for=20
inline classes.
>
> My proposed solution is to introduce a new type for each inline class=20
> if you try to re-use some shared inline type.
> class C {
> inline class X : public P { } x; // Instantiate P<C, 0>
> inline class Y : public P { } y; // Instantiate P<C, 4>
> inline class Z {
> // ...
> } z1, z2; // ill-formed: only one instance per type
> void baz();
> };
> C c;
> auto& x =3D c.x;
> auto& y =3D c.y;
> x.get();
> y.get();
> Now there is no longer an ambiguity and it is clearly evident from the=20
> fact that X and Y are distinct types in the code.=20
IMHO, this doesn't solves the problem.
> If someone cames up with a terser syntax that doesn't require=20
> repeating the inheritence chain in every place, that would be awesome.
>
> class D {
> inline class : public P { } x, y; // ill-formed: only one inline=20
> subobject per type
> inline class : public P { } x*; // ill-formed
> inline class : public P { } x[5]; // probably ill-formed because=20
> (&x[0] - this) !=3D (&x[1] - this)
> };
>
> *(3)* If I were to ignore the possibility of properties then I'd be in=20
> favor of obtaining references/pointers to inline classes. For outside=20
> code duck typing makes them behave just like references to regular=20
> subobjects. A problem still remains with the address equality of=20
> multiple empty inline subobjects if they had zero size.
>
> If viewed under the light of a possible implementation for properties=20
> then it may become a leaking abstraction. However I believe there is=20
> as much value in passing a property by-reference as is passing a=20
> regular value by-reference. It allows the callee modifying access=20
> (whether that is good or bad is left undecided). Assuming properties=20
> were implicitly convertible to the underlying type (by whichever=20
> means) then a function expecting the underlying type invokes the=20
> conversion, whereas argument deduction results in the inline subobject=20
> backing the property. This can cause ambiguity depending on whether=20
> the inline class represents a property or not.
>
> class A {
> // x is not supposed to be a property
> inline class X { ... } x;
> // y is supposed to be a property
> inline class Y { operator int(); Y& operator=3D(int); ... } y;
> };
> template<class T> void foo(T);
> A a;
> foo(a.x); // ill-formed, cannot copy inline class
Why do you deduce that the property must be copied? Shouldn't T deduce=20
to X&.
However
void foo(A::X);
must be ill formed.
> foo(a.y); // also ill-formed but shouldn't be: the intention is to=20
> pass a.y's value
>
> A possible way of clearing this ambiguity is with a contextual(?) keyword
> class A {
> // x is not supposed to be a property
> inline class X { ... } x;
> // y is supposed to be a property
> // "property int" replaces the "inline class..." boilerplate
> property int y { /* default implementation get/set auto-generated=20
> where not user-provided */ };
I don't see the advantage of having properties that behave as raw data=20
member, but this is out of the scope of the thread.
> };
> template<class T> void foo(T);
> A a;
> foo(a.x); // ill-formed, cannot copy inline class
> foo(a.y); // instantiation ill-formed at first, but it's a property so=20
> try again with the implicit int conversion
>
> When it comes to references, then as already stated the property type=20
> is expected to be implicitly convertible to/assignable from an=20
> instance of its underlying type. So even if argument deduction were to=20
> prefer the inline class's type, the property still behaves under duck=20
> typing as-if it were the value. Concretely in the following code:
>
> tempalte<class T>
> void foo(T& prop) { prop =3D 5; }
>
> It should be irrelevant whether T deduces to "int" or=20
> "MyHealthProperty<Player, 32>".
>
> But it is more difficult with pointers. Whereas you can assign an int&=20
> to a MyHealthProperty<Player, 32>& you cannot assign a int* to a=20
> MyHealthProperty<Player, 32>*. Therefore it makes a pretty big=20
> difference whether one deduces int* or MyHealthProperty<Player, 32>*.=20
> But taking the address of the result of a property's getter is only=20
> possible if it doesn't return an rvalue so this remains an open problem.
>
>
>
> Well that took longer than expected. And now I realize that this still=20
> overlaps with the properties thread. I guess that won't change unless=20
> it is decided that inline classes are not the right tool to implement=20
> them. Sigh.
>
Thanks for the long post.I see two major issues up to now:
* managing with several instances of the same inline class implies=20
either having different types or including a run-time overhead.
* passing inline class instances by reference transforms any such=20
function in a template if the OFFSET is part of the type.
Storing the offset on the inline class solves both issues, but introduce=20
a new one :(
Vicente
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Jeremy Maitin-Shepard <jeremy@jeremyms.com>
Date: Tue, 1 Sep 2015 11:18:42 -0700
Raw View
--94eb2c07b5507f67df051eb394b3
Content-Type: text/plain; charset=UTF-8
On Tue, Sep 1, 2015 at 7:59 AM, Matthew Woehlke <mwoehlke.floss@gmail.com>
wrote:
> On 2015-09-01 00:05, Jeremy Maitin-Shepard wrote:
> > It seems the discussion here is touching on a bunch of a features that
> > would be potentially very useful:
> >
> > 1. Zero-size class data members.
> >
> > 2. Java-style inner classes: this could be done by allowing the
> definition
> > of classes with lambda-style capture specifications. It could be allowed
> > as a special case that if only a single reference or non-mutable pointer
> is
> > captured and there are no data members, then the extra level of
> indirection
> > is skipped, such that a pointer to the inner class is exactly the
> captured
> > pointer. Maybe this same optimization could also be supported in other
> > cases.
> >
> > 3. Expression aliases, so that a property proxy object can be accessed
> > using x.property rather than x.property().
> >
> > In my view combining (2) and (3) to get properties is much cleaner, and
> > brings us a lot more generally useful functionality, than the inline
> class
> > idea. (1) wouldn't be needed for properties but would certainly save a
> lot
> > of metaprogramming hassle generally.
>
> How is (3) relevant? If you implement properties like (2), then I assume
> you are still implementing them something like an inner class with
> get/set methods to represent the property. In which case, wouldn't that
> class just be a member?
>
>
As I see it, sizeof(CapturingClass) would be the size of a pointer to the
outer class, so you couldn't embed it as a zero-size member.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--94eb2c07b5507f67df051eb394b3
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Tue, Sep 1, 2015 at 7:59 AM, Matthew Woehlke <span dir=
=3D"ltr"><<a href=3D"mailto:mwoehlke.floss@gmail.com" target=3D"_blank">=
mwoehlke.floss@gmail.com</a>></span> wrote:<br><div class=3D"gmail_extra=
"><div class=3D"gmail_quote"><blockquote class=3D"gmail_quote" style=3D"mar=
gin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class=3D"=
">On 2015-09-01 00:05, Jeremy Maitin-Shepard wrote:<br>
> It seems the discussion here is touching on a bunch of a features that=
<br>
> would be potentially very useful:<br>
><br>
> 1. Zero-size class data members.<br>
><br>
> 2. Java-style inner classes: this could be done by allowing the defini=
tion<br>
> of classes with lambda-style capture specifications.=C2=A0 It could be=
allowed<br>
> as a special case that if only a single reference or non-mutable point=
er is<br>
> captured and there are no data members, then the extra level of indire=
ction<br>
> is skipped, such that a pointer to the inner class is exactly the capt=
ured<br>
> pointer.=C2=A0 Maybe this same optimization could also be supported in=
other<br>
> cases.<br>
><br>
> 3. Expression aliases, so that a property proxy object can be accessed=
<br>
> using x.property rather than x.property().<br>
><br>
> In my view combining (2) and (3) to get properties is much cleaner, an=
d<br>
> brings us a lot more generally useful functionality, than the inline c=
lass<br>
> idea.=C2=A0 (1) wouldn't be needed for properties but would certai=
nly save a lot<br>
> of metaprogramming hassle generally.<br>
<br>
</span>How is (3) relevant? If you implement properties like (2), then I as=
sume<br>
you are still implementing them something like an inner class with<br>
get/set methods to represent the property. In which case, wouldn't that=
<br>
class just be a member?<br>
<span class=3D"HOEnZb"></span><br></blockquote></div><br></div><div class=
=3D"gmail_extra">As I see it, sizeof(CapturingClass) would be the size of a=
pointer to the outer class, so you couldn't embed it as a zero-size me=
mber.<br></div></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--94eb2c07b5507f67df051eb394b3--
.
Author: "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr>
Date: Tue, 1 Sep 2015 20:20:27 +0200
Raw View
This is a multi-part message in MIME format.
--------------050704070409050302080702
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: quoted-printable
Le 01/09/15 20:02, Matthew Woehlke a =C3=A9crit :
> On 2015-09-01 13:27, Vicente J. Botet Escriba wrote:
>> I believe that inline classes can be useful independently of whether its
>> size if 0 when there is no data. While this could have its importance
>> for properties without storage, it seems less important to me when using
>> inline classes as services or subjects of an object as them use to carry
>> some data.
> I fail to see the value of an "inline" class vs. a normal class if they
> *don't* provide the zero-storage guarantee. At that point, all you've
> done is avoid the back-reference pointer to the containing class.
Well this is already a good thing. But not only, the inline class makes=20
clear the intent..
> Which
> you could do anyway with pointer arithmetic, which moreover should be
> equally efficient as if it was a compiler feature.
>
> Conversely, trying to maintain the utility of inline classes *without*
> the zero-storage guarantee makes them significantly more complicated to
> implement.
>
Could you explain why?
Vicente
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
--------------050704070409050302080702
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<html>
<head>
<meta content=3D"text/html; charset=3Dutf-8" http-equiv=3D"Content-Type=
">
</head>
<body text=3D"#000000" bgcolor=3D"#FFFFFF">
<div class=3D"moz-cite-prefix">Le 01/09/15 20:02, Matthew Woehlke a
=C3=A9crit=C2=A0:<br>
</div>
<blockquote cite=3D"mid:ms4p80$tg2$1@ger.gmane.org" type=3D"cite">
<pre wrap=3D"">On 2015-09-01 13:27, Vicente J. Botet Escriba wrote:
</pre>
<blockquote type=3D"cite">
<pre wrap=3D"">I believe that inline classes can be useful independ=
ently of whether its
size if 0 when there is no data. While this could have its importance
for properties without storage, it seems less important to me when using
inline classes as services or subjects of an object as them use to carry
some data.
</pre>
</blockquote>
<pre wrap=3D"">
I fail to see the value of an "inline" class vs. a normal class if they
*don't* provide the zero-storage guarantee. At that point, all you've
done is avoid the back-reference pointer to the containing class. </pre>
</blockquote>
Well this is already a good thing. But not only, the inline class
makes clear the intent..<br>
<blockquote cite=3D"mid:ms4p80$tg2$1@ger.gmane.org" type=3D"cite">
<pre wrap=3D"">Which
you could do anyway with pointer arithmetic, which moreover should be
equally efficient as if it was a compiler feature.
Conversely, trying to maintain the utility of inline classes *without*
the zero-storage guarantee makes them significantly more complicated to
implement.
</pre>
</blockquote>
<font size=3D"+1">Could you explain why?</font><br>
<br>
Vicente<br>
</body>
</html>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--------------050704070409050302080702--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Tue, 01 Sep 2015 14:28:35 -0400
Raw View
On 2015-08-31 15:25, Bjorn Reese wrote:
> I frequently come across use-cases where inline classes can be useful.
>
> One use-case is to compensate for the lack of partial template
> specialization of class member functions. A workaround is to introduce
> a functor with static member functions, which are passed the class
> instance as a parameter, because the functor needs to operate on the
> class member variables. For example:
>
> class value
> {
> public:
> template <typename T>
> T convert() const;
> };
>
> Say that need a specialization for value::convert<T>() for integral
> types. This can be done via the following functor:
>
> template <typename T, typename Enable = void>
> struct value_functor {};
>
> template <typename T>
> struct value_functor<T, enable_if<is_integral<T>>::type>
> {
> static T convert(const value& v)
> {
> // Do integral conversion here
> }
> };
>
> This is invoked as follows:
>
> template <typename T>
> T value::convert() const
> {
> return value_functor<ReturnType>::convert(*this);
> }
>
> Furthermore, if value_functor must access private members in the value
> class, then it must become a friend of the class.
>
> An inline class, as I have understood it, will handle the above case
> more elegantly.
Do you mean like this?
class Value
{
public:
template <typename T> inline class Conversion
{
// ...generic implementation...
}
template <typename T> Conversion<T> convert;
};
template <typename T>
inline class value::conversion<T, enable_if<is_integral<T>>::type>
{
T operator()() const { /* access members of Value here */ }
};
Value v;
long x = v.convert<long>();
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Tue, 01 Sep 2015 14:33:36 -0400
Raw View
On 2015-09-01 14:20, Vicente J. Botet Escriba wrote:
> Le 01/09/15 20:02, Matthew Woehlke a =C3=A9crit :
>> Conversely, trying to maintain the utility of inline classes *without*
>> the zero-storage guarantee makes them significantly more complicated to
>> implement.
>
> Could you explain why?
I think I've addressed this sufficiently already that I'm not going to
repeat myself. The main point is that being zero-sized *is*, IMO, one of
the primary (if not *the*) motivating features. Once you allow them to
maybe-or-maybe-not be zero sized, you have to deal with all sorts of
special cases depending whether they are or not, what happens when you
have more than one instance, and so forth. In short, you either
sacrifice a major rationale or make the proposal much, much more
complicated.
--=20
Matthew
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Miro Knejp <miro.knejp@gmail.com>
Date: Tue, 1 Sep 2015 20:40:07 +0200
Raw View
--Apple-Mail=_DB83D42B-278F-4D12-AED1-46FDD97560A9
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=UTF-8
> On 01 Sep 2015, at 20:15 , Vicente J. Botet Escriba <vicente.botet@wanado=
o.fr> wrote:
>=20
> I don't see the trouble copying inline class instances between two outer =
class instances if they are of the good type.
Yes, that=E2=80=99s fine.
>> Con:
>> * If empty inline subobjects have no storage then multiple instances can=
have the same address.
> I believe that this is orthogonal and related to the possibility of havin=
g object of size 0, which should be taken by another proposal.
>> * In the context of properties (under the assumption they are implemente=
d with this technique) it is potentially a leaking abstraction. Other langu=
ages don't allow you to pass around the actual property itself, only the re=
sult of their getter. But then, these languages usually don't have the conc=
ept of references. It might be an interesting tool to pass a property by-re=
ference to a function and have that function operate on the property instea=
d of only its value. But it poses the ambiguity of when to deduce the prope=
rty object and when the property's value.
> I don't seethe problem. please could you clarify?
If you have a function template<class T> void foo(const T&) then the deduct=
ion results in the inline class=E2=80=99s type for T. However if T represen=
ts a property then you have now exposed the property implementation detail.=
I don=E2=80=99t know if this is really a problem, but some have argued it =
is a leaky abstraction and thus undesireable.
>>=20
>> *(4)* Should empty inline subobjects have storage?
> Lets say no for the time being.
>> Pro: zero overhead
>> Con: see (3)
>>=20
>> *(5)* Should separate inline subobjects be allowed to have the same type=
?
> yes.
>> Pro: It's convenient.
>> Con:
>> * If they aren't empty and access their state in member functions the co=
mpiler must find a way to distinguish them. Either by storing a hidden offs=
et or by instantiating different actual types. Both options aren't zero ove=
rhead either in object size or code size.
> Right.
>> * If every inline subobject has a different type and (3) is resolved as =
yes then it is possible to safely static_cast between the containing instan=
ce and the inline subobject.
>>=20
> I don't understand this cons.
It allows you to do=20
A a;
auto& x =3D static_cast<A::X&>(a);
auto& y =3D static_cast<A::Y&>(a);
auto& a2 =3D static_cast<A&>(x);
auto& a3 =3D static_cast<A&>(y);
But only if A.x and A.y have different types. If they have the same type it=
only works if a.x and a.y somehow know their offset inside A.
>> *(6)* It should be possible to declare re-usable inline classes outside =
of the containing class. This means these inline classes defined at namespa=
ce scope must be based on template instantiations as they are only complete=
types when embedded in a containing class and final name lookup and offset=
-patching can take place.
> I would say this will be nice, but I could live without.
>>=20
>> Here are my attempts at solving some of the above problems.
>> Keep in mind that this is largely pseudocode so please don't get riled u=
p on the syntax again.
>>=20
>> *(6)* I'll give this a try first because it is not dependent on others:
>>=20
>> inline class P {
>> int i;
>> public:
>> void set(int j) { i =3D j; }
>> int get() { return i; }
>> void bar() { this->baz(); }
>> };
>> In a definition like the above "this" is always dependent because its ty=
pe is unknown until the inline class is embedded in an actual containing cl=
ass. This is the same name lookup behavior as in classes with a dependent b=
ase class, making it consistent with existing practice and the suggested na=
me lookup for inline subobjects. It is basically syntax for compiler-genera=
ted CRTP (which is a useful pattern on its own and this might actually make=
it more accessible).
>> It is then used like
>> class A {
>> P x; // Instantiate P<A, 0> with containing class=3DA and offset=3D0
>> void baz();
>> };
>> A a;
>> a.x.bar(); // results in a.baz()
>>=20
>> Because the so defined inline class is incomplete and inherently a templ=
ate a function involving such a type by reference or pointer (assuming (3) =
is allowed) in its signature becomes a template function.
> Agreed.
>>=20
>>=20
>> *(5)* My answer here is no, there cannot be two inline subobjects of the=
same type.
>>=20
>> The problem:
>> class B {
>> P x, y;
>> void baz();
>> };
>> B b;
>> auto& x =3D b.x;
>> auto& y =3D b.y;
>> x.get();
>> y.get();
>> This poses a conflict because x and y have different offsets. The compil=
er has to either store the offset inside b.x and b.y or generate different =
types for x and y with different get/set implementations. The latter result=
s in references to B::x and B::y to be incompatible, which is surprising be=
cause from the code they seem to have the same.
> You are right that they are incompatible in the current standard if diffe=
rent types are generated. We need to find a specific solution for inline cl=
asses.
>>=20
>> My proposed solution is to introduce a new type for each inline class if=
you try to re-use some shared inline type.
>> class C {
>> inline class X : public P { } x; // Instantiate P<C, 0>
>> inline class Y : public P { } y; // Instantiate P<C, 4>
>> inline class Z {
>> // ...
>> } z1, z2; // ill-formed: only one instance per type
>> void baz();
>> };
>> C c;
>> auto& x =3D c.x;
>> auto& y =3D c.y;
>> x.get();
>> y.get();
>> Now there is no longer an ambiguity and it is clearly evident from the f=
act that X and Y are distinct types in the code.=20
> IMHO, this doesn't solves the problem.
It technically does, but it=E2=80=99s very verbose. I=E2=80=99d really like=
something that=E2=80=99s a bit more user friendly.
>>=20
> Why do you deduce that the property must be copied? Shouldn't T deduce to=
X&.
> However
> void foo(A::X);
>=20
> must be ill formed.
Template parameters don=E2=80=99t deduce to references. You need to explici=
tly write foo(T&) if you want a lvalue reference parameter.
>> foo(a.y); // also ill-formed but shouldn't be: the intention is to pass =
a.y's value
>>=20
>> A possible way of clearing this ambiguity is with a contextual(?) keywor=
d
>> class A {
>> // x is not supposed to be a property
>> inline class X { ... } x;
>> // y is supposed to be a property
>> // "property int" replaces the "inline class..." boilerplate
>> property int y { /* default implementation get/set auto-generated where=
not user-provided */ };
> I don't see the advantage of having properties that behave as raw data me=
mber, but this is out of the scope of the thread.
I often see properties where only the setter is user-defined and the getter=
is left unchanged. It has its uses. But these languages (C#, Obj-C) also h=
ave some syntax/convention to assign to the actual backing variable (which =
the auto-generated getter is reading) from within the setter. If we don=E2=
=80=99t get that the usefulness drops significantly.
>> };
>> template<class T> void foo(T);
>> A a;
>> foo(a.x); // ill-formed, cannot copy inline class
>> foo(a.y); // instantiation ill-formed at first, but it's a property so t=
ry again with the implicit int conversion
>>=20
>> When it comes to references, then as already stated the property type is=
expected to be implicitly convertible to/assignable from an instance of it=
s underlying type. So even if argument deduction were to prefer the inline =
class's type, the property still behaves under duck typing as-if it were th=
e value. Concretely in the following code:
>>=20
>> tempalte<class T>
>> void foo(T& prop) { prop =3D 5; }
>>=20
>> It should be irrelevant whether T deduces to "int" or "MyHealthProperty<=
Player, 32>".
>>=20
>> But it is more difficult with pointers. Whereas you can assign an int& t=
o a MyHealthProperty<Player, 32>& you cannot assign a int* to a MyHealthPro=
perty<Player, 32>*. Therefore it makes a pretty big difference whether one =
deduces int* or MyHealthProperty<Player, 32>*. But taking the address of th=
e result of a property's getter is only possible if it doesn't return an rv=
alue so this remains an open problem.
>>=20
>>=20
>>=20
>> Well that took longer than expected. And now I realize that this still o=
verlaps with the properties thread. I guess that won't change unless it is =
decided that inline classes are not the right tool to implement them. Sigh.
>>=20
> Thanks for the long post.I see two major issues up to now:
> * managing with several instances of the same inline class implies either=
having different types or including a run-time overhead.
> * passing inline class instances by reference transforms any such functio=
n in a template if the OFFSET is part of the type.
>=20
> Storing the offset on the inline class solves both issues, but introduce =
a new one :(
>=20
>=20
> Vicente
>=20
> --=20
>=20
> --- You received this message because you are subscribed to the Google Gr=
oups "ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this group and stop receiving emails from it, send an=
email to std-proposals+unsubscribe@isocpp.org <mailto:std-proposals+unsubs=
cribe@isocpp.org>.
> To post to this group, send email to std-proposals@isocpp.org <mailto:std=
-proposals@isocpp.org>.
> Visit this group at http://groups.google.com/a/isocpp.org/group/std-propo=
sals/ <http://groups.google.com/a/isocpp.org/group/std-proposals/>.
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
--Apple-Mail=_DB83D42B-278F-4D12-AED1-46FDD97560A9
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset=UTF-8
<html><head><meta http-equiv=3D"Content-Type" content=3D"text/html charset=
=3Dutf-8"></head><body style=3D"word-wrap: break-word; -webkit-nbsp-mode: s=
pace; -webkit-line-break: after-white-space;" class=3D""><br class=3D""><di=
v><blockquote type=3D"cite" class=3D""><div class=3D"">On 01 Sep 2015, at 2=
0:15 , Vicente J. Botet Escriba <<a href=3D"mailto:vicente.botet@wanadoo=
..fr" class=3D"">vicente.botet@wanadoo.fr</a>> wrote:</div><br class=3D"A=
pple-interchange-newline"><div class=3D""><span style=3D"font-family: Helve=
tica; font-size: 12px; font-style: normal; font-variant: normal; font-weigh=
t: normal; letter-spacing: normal; line-height: normal; orphans: auto; text=
-align: start; text-indent: 0px; text-transform: none; white-space: normal;=
widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: no=
ne; display: inline !important;" class=3D"">I don't see the trouble copying=
inline class instances between two outer class instances if they are of th=
e good type.</span><br style=3D"font-family: Helvetica; font-size: 12px; fo=
nt-style: normal; font-variant: normal; font-weight: normal; letter-spacing=
: normal; line-height: normal; orphans: auto; text-align: start; text-inden=
t: 0px; text-transform: none; white-space: normal; widows: auto; word-spaci=
ng: 0px; -webkit-text-stroke-width: 0px;" class=3D""></div></blockquote>Yes=
, that=E2=80=99s fine.<br class=3D""><blockquote type=3D"cite" class=3D""><=
div class=3D""><blockquote type=3D"cite" style=3D"font-family: Helvetica; f=
ont-size: 12px; font-style: normal; font-variant: normal; font-weight: norm=
al; letter-spacing: normal; line-height: normal; orphans: auto; text-align:=
start; text-indent: 0px; text-transform: none; white-space: normal; widows=
: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=3D"">Con:=
<br class=3D"">* If empty inline subobjects have no storage then multiple i=
nstances can have the same address.<br class=3D""></blockquote><span style=
=3D"font-family: Helvetica; font-size: 12px; font-style: normal; font-varia=
nt: normal; font-weight: normal; letter-spacing: normal; line-height: norma=
l; orphans: auto; text-align: start; text-indent: 0px; text-transform: none=
; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke=
-width: 0px; float: none; display: inline !important;" class=3D"">I believe=
that this is orthogonal and related to the possibility of having object of=
size 0, which should be taken by another proposal.</span><br style=3D"font=
-family: Helvetica; font-size: 12px; font-style: normal; font-variant: norm=
al; font-weight: normal; letter-spacing: normal; line-height: normal; orpha=
ns: auto; text-align: start; text-indent: 0px; text-transform: none; white-=
space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: =
0px;" class=3D""><blockquote type=3D"cite" style=3D"font-family: Helvetica;=
font-size: 12px; font-style: normal; font-variant: normal; font-weight: no=
rmal; letter-spacing: normal; line-height: normal; orphans: auto; text-alig=
n: start; text-indent: 0px; text-transform: none; white-space: normal; wido=
ws: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=3D"">* =
In the context of properties (under the assumption they are implemented wit=
h this technique) it is potentially a leaking abstraction. Other languages =
don't allow you to pass around the actual property itself, only the result =
of their getter. But then, these languages usually don't have the concept o=
f references. It might be an interesting tool to pass a property by-referen=
ce to a function and have that function operate on the property instead of =
only its value. But it poses the ambiguity of when to deduce the property o=
bject and when the property's value.<br class=3D""></blockquote><span style=
=3D"font-family: Helvetica; font-size: 12px; font-style: normal; font-varia=
nt: normal; font-weight: normal; letter-spacing: normal; line-height: norma=
l; orphans: auto; text-align: start; text-indent: 0px; text-transform: none=
; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke=
-width: 0px; float: none; display: inline !important;" class=3D"">I don't s=
eethe problem. please could you clarify?</span><br style=3D"font-family: He=
lvetica; font-size: 12px; font-style: normal; font-variant: normal; font-we=
ight: normal; letter-spacing: normal; line-height: normal; orphans: auto; t=
ext-align: start; text-indent: 0px; text-transform: none; white-space: norm=
al; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=
=3D""></div></blockquote>If you have a function template<class T> voi=
d foo(const T&) then the deduction results in the inline class=E2=80=99=
s type for T. However if T represents a property then you have now exposed =
the property implementation detail. I don=E2=80=99t know if this is really =
a problem, but some have argued it is a leaky abstraction and thus undesire=
able.<br class=3D""><blockquote type=3D"cite" class=3D""><div class=3D""><b=
lockquote type=3D"cite" style=3D"font-family: Helvetica; font-size: 12px; f=
ont-style: normal; font-variant: normal; font-weight: normal; letter-spacin=
g: normal; line-height: normal; orphans: auto; text-align: start; text-inde=
nt: 0px; text-transform: none; white-space: normal; widows: auto; word-spac=
ing: 0px; -webkit-text-stroke-width: 0px;" class=3D""><br class=3D"">*(4)* =
Should empty inline subobjects have storage?<br class=3D""></blockquote><sp=
an style=3D"font-family: Helvetica; font-size: 12px; font-style: normal; fo=
nt-variant: normal; font-weight: normal; letter-spacing: normal; line-heigh=
t: normal; orphans: auto; text-align: start; text-indent: 0px; text-transfo=
rm: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-tex=
t-stroke-width: 0px; float: none; display: inline !important;" class=3D"">L=
ets say no for the time being.</span><br style=3D"font-family: Helvetica; f=
ont-size: 12px; font-style: normal; font-variant: normal; font-weight: norm=
al; letter-spacing: normal; line-height: normal; orphans: auto; text-align:=
start; text-indent: 0px; text-transform: none; white-space: normal; widows=
: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=3D""><blo=
ckquote type=3D"cite" style=3D"font-family: Helvetica; font-size: 12px; fon=
t-style: normal; font-variant: normal; font-weight: normal; letter-spacing:=
normal; line-height: normal; orphans: auto; text-align: start; text-indent=
: 0px; text-transform: none; white-space: normal; widows: auto; word-spacin=
g: 0px; -webkit-text-stroke-width: 0px;" class=3D"">Pro: zero overhead<br c=
lass=3D"">Con: see (3)<br class=3D""><br class=3D"">*(5)* Should separate i=
nline subobjects be allowed to have the same type?<br class=3D""></blockquo=
te><span style=3D"font-family: Helvetica; font-size: 12px; font-style: norm=
al; font-variant: normal; font-weight: normal; letter-spacing: normal; line=
-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-t=
ransform: none; white-space: normal; widows: auto; word-spacing: 0px; -webk=
it-text-stroke-width: 0px; float: none; display: inline !important;" class=
=3D"">yes.</span><br style=3D"font-family: Helvetica; font-size: 12px; font=
-style: normal; font-variant: normal; font-weight: normal; letter-spacing: =
normal; line-height: normal; orphans: auto; text-align: start; text-indent:=
0px; text-transform: none; white-space: normal; widows: auto; word-spacing=
: 0px; -webkit-text-stroke-width: 0px;" class=3D""><blockquote type=3D"cite=
" style=3D"font-family: Helvetica; font-size: 12px; font-style: normal; fon=
t-variant: normal; font-weight: normal; letter-spacing: normal; line-height=
: normal; orphans: auto; text-align: start; text-indent: 0px; text-transfor=
m: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text=
-stroke-width: 0px;" class=3D"">Pro: It's convenient.<br class=3D"">Con:<br=
class=3D"">* If they aren't empty and access their state in member functio=
ns the compiler must find a way to distinguish them. Either by storing a hi=
dden offset or by instantiating different actual types. Both options aren't=
zero overhead either in object size or code size.<br class=3D""></blockquo=
te><span style=3D"font-family: Helvetica; font-size: 12px; font-style: norm=
al; font-variant: normal; font-weight: normal; letter-spacing: normal; line=
-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-t=
ransform: none; white-space: normal; widows: auto; word-spacing: 0px; -webk=
it-text-stroke-width: 0px; float: none; display: inline !important;" class=
=3D"">Right.</span><br style=3D"font-family: Helvetica; font-size: 12px; fo=
nt-style: normal; font-variant: normal; font-weight: normal; letter-spacing=
: normal; line-height: normal; orphans: auto; text-align: start; text-inden=
t: 0px; text-transform: none; white-space: normal; widows: auto; word-spaci=
ng: 0px; -webkit-text-stroke-width: 0px;" class=3D""><blockquote type=3D"ci=
te" style=3D"font-family: Helvetica; font-size: 12px; font-style: normal; f=
ont-variant: normal; font-weight: normal; letter-spacing: normal; line-heig=
ht: normal; orphans: auto; text-align: start; text-indent: 0px; text-transf=
orm: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-te=
xt-stroke-width: 0px;" class=3D"">* If every inline subobject has a differe=
nt type and (3) is resolved as yes then it is possible to safely static_cas=
t between the containing instance and the inline subobject.<br class=3D""><=
br class=3D""></blockquote><span style=3D"font-family: Helvetica; font-size=
: 12px; font-style: normal; font-variant: normal; font-weight: normal; lett=
er-spacing: normal; line-height: normal; orphans: auto; text-align: start; =
text-indent: 0px; text-transform: none; white-space: normal; widows: auto; =
word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: in=
line !important;" class=3D"">I don't understand this cons.</span><br style=
=3D"font-family: Helvetica; font-size: 12px; font-style: normal; font-varia=
nt: normal; font-weight: normal; letter-spacing: normal; line-height: norma=
l; orphans: auto; text-align: start; text-indent: 0px; text-transform: none=
; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke=
-width: 0px;" class=3D""></div></blockquote>It allows you to do </div>=
<div>A a;</div><div>auto& x =3D static_cast<A::X&>(a);</div><=
div>auto& y =3D static_cast<A::Y&>(a);</div><div>auto& a2=
=3D static_cast<A&>(x);</div><div><div>auto& a3 =3D static_c=
ast<A&>(y);</div><div class=3D""><br class=3D""></div></div><div>=
But only if A.x and A.y have different types. If they have the same type it=
only works if a.x and a.y somehow know their offset inside A.</div><div><b=
r class=3D""><blockquote type=3D"cite" class=3D""><div class=3D""></div></b=
lockquote><blockquote type=3D"cite" class=3D""><div class=3D""><blockquote =
type=3D"cite" style=3D"font-family: Helvetica; font-size: 12px; font-style:=
normal; font-variant: normal; font-weight: normal; letter-spacing: normal;=
line-height: normal; orphans: auto; text-align: start; text-indent: 0px; t=
ext-transform: none; white-space: normal; widows: auto; word-spacing: 0px; =
-webkit-text-stroke-width: 0px;" class=3D"">*(6)* It should be possible to =
declare re-usable inline classes outside of the containing class. This mean=
s these inline classes defined at namespace scope must be based on template=
instantiations as they are only complete types when embedded in a containi=
ng class and final name lookup and offset-patching can take place.<br class=
=3D""></blockquote><span style=3D"font-family: Helvetica; font-size: 12px; =
font-style: normal; font-variant: normal; font-weight: normal; letter-spaci=
ng: normal; line-height: normal; orphans: auto; text-align: start; text-ind=
ent: 0px; text-transform: none; white-space: normal; widows: auto; word-spa=
cing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !im=
portant;" class=3D"">I would say this will be nice, but I could live withou=
t.</span><br style=3D"font-family: Helvetica; font-size: 12px; font-style: =
normal; font-variant: normal; font-weight: normal; letter-spacing: normal; =
line-height: normal; orphans: auto; text-align: start; text-indent: 0px; te=
xt-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -=
webkit-text-stroke-width: 0px;" class=3D""><blockquote type=3D"cite" style=
=3D"font-family: Helvetica; font-size: 12px; font-style: normal; font-varia=
nt: normal; font-weight: normal; letter-spacing: normal; line-height: norma=
l; orphans: auto; text-align: start; text-indent: 0px; text-transform: none=
; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke=
-width: 0px;" class=3D""><br class=3D"">Here are my attempts at solving som=
e of the above problems.<br class=3D"">Keep in mind that this is largely ps=
eudocode so please don't get riled up on the syntax again.<br class=3D""><b=
r class=3D"">*(6)* I'll give this a try first because it is not dependent o=
n others:<br class=3D""><br class=3D"">inline class P {<br class=3D""> =
;int i;<br class=3D"">public:<br class=3D""> void set(int j) { i =3D j=
; }<br class=3D""> int get() { return i; }<br class=3D""> void ba=
r() { this->baz(); }<br class=3D"">};<br class=3D"">In a definition like=
the above "this" is always dependent because its type is unknown until the=
inline class is embedded in an actual containing class. This is the same n=
ame lookup behavior as in classes with a dependent base class, making it co=
nsistent with existing practice and the suggested name lookup for inline su=
bobjects. It is basically syntax for compiler-generated CRTP (which is a us=
eful pattern on its own and this might actually make it more accessible).<b=
r class=3D"">It is then used like<br class=3D"">class A {<br class=3D"">&nb=
sp;P x; // Instantiate P<A, 0> with containing class=3DA and offset=
=3D0<br class=3D""> void baz();<br class=3D"">};<br class=3D"">A a;<br=
class=3D"">a.x.bar(); // results in a.baz()<br class=3D""><br class=3D"">B=
ecause the so defined inline class is incomplete and inherently a template =
a function involving such a type by reference or pointer (assuming (3) is a=
llowed) in its signature becomes a template function.<br class=3D""></block=
quote><span style=3D"font-family: Helvetica; font-size: 12px; font-style: n=
ormal; font-variant: normal; font-weight: normal; letter-spacing: normal; l=
ine-height: normal; orphans: auto; text-align: start; text-indent: 0px; tex=
t-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -w=
ebkit-text-stroke-width: 0px; float: none; display: inline !important;" cla=
ss=3D"">Agreed.</span><br style=3D"font-family: Helvetica; font-size: 12px;=
font-style: normal; font-variant: normal; font-weight: normal; letter-spac=
ing: normal; line-height: normal; orphans: auto; text-align: start; text-in=
dent: 0px; text-transform: none; white-space: normal; widows: auto; word-sp=
acing: 0px; -webkit-text-stroke-width: 0px;" class=3D""><blockquote type=3D=
"cite" style=3D"font-family: Helvetica; font-size: 12px; font-style: normal=
; font-variant: normal; font-weight: normal; letter-spacing: normal; line-h=
eight: normal; orphans: auto; text-align: start; text-indent: 0px; text-tra=
nsform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit=
-text-stroke-width: 0px;" class=3D""><br class=3D""><br class=3D"">*(5)* My=
answer here is no, there cannot be two inline subobjects of the same type.=
<br class=3D""><br class=3D"">The problem:<br class=3D"">class B {<br class=
=3D""> P x, y;<br class=3D""> void baz();<br class=3D"">};<br cla=
ss=3D"">B b;<br class=3D"">auto& x =3D b.x;<br class=3D"">auto& y =
=3D b.y;<br class=3D"">x.get();<br class=3D"">y.get();<br class=3D"">This p=
oses a conflict because x and y have different offsets. The compiler has to=
either store the offset inside b.x and b.y or generate different types for=
x and y with different get/set implementations. The latter results in refe=
rences to B::x and B::y to be incompatible, which is surprising because fro=
m the code they seem to have the same.<br class=3D""></blockquote><span sty=
le=3D"font-family: Helvetica; font-size: 12px; font-style: normal; font-var=
iant: normal; font-weight: normal; letter-spacing: normal; line-height: nor=
mal; orphans: auto; text-align: start; text-indent: 0px; text-transform: no=
ne; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stro=
ke-width: 0px; float: none; display: inline !important;" class=3D"">You are=
right that they are incompatible in the current standard if different type=
s are generated. We need to find a specific solution for inline classes.</s=
pan><br style=3D"font-family: Helvetica; font-size: 12px; font-style: norma=
l; font-variant: normal; font-weight: normal; letter-spacing: normal; line-=
height: normal; orphans: auto; text-align: start; text-indent: 0px; text-tr=
ansform: none; white-space: normal; widows: auto; word-spacing: 0px; -webki=
t-text-stroke-width: 0px;" class=3D""><blockquote type=3D"cite" style=3D"fo=
nt-family: Helvetica; font-size: 12px; font-style: normal; font-variant: no=
rmal; font-weight: normal; letter-spacing: normal; line-height: normal; orp=
hans: auto; text-align: start; text-indent: 0px; text-transform: none; whit=
e-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width=
: 0px;" class=3D""><br class=3D"">My proposed solution is to introduce a ne=
w type for each inline class if you try to re-use some shared inline type.<=
br class=3D"">class C {<br class=3D""> inline class X : public P { } x=
; // Instantiate P<C, 0><br class=3D""> inline class Y : public =
P { } y; // Instantiate P<C, 4><br class=3D""> inline class Z {<=
br class=3D""> // ...<br class=3D""> } z1, z2; // ill=
-formed: only one instance per type<br class=3D""> void baz();<br clas=
s=3D"">};<br class=3D"">C c;<br class=3D"">auto& x =3D c.x;<br class=3D=
"">auto& y =3D c.y;<br class=3D"">x.get();<br class=3D"">y.get();<br cl=
ass=3D"">Now there is no longer an ambiguity and it is clearly evident from=
the fact that X and Y are distinct types in the code.<span class=3D"Apple-=
converted-space"> </span><br class=3D""></blockquote><span style=3D"fo=
nt-family: Helvetica; font-size: 12px; font-style: normal; font-variant: no=
rmal; font-weight: normal; letter-spacing: normal; line-height: normal; orp=
hans: auto; text-align: start; text-indent: 0px; text-transform: none; whit=
e-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width=
: 0px; float: none; display: inline !important;" class=3D"">IMHO, this does=
n't solves the problem.</span><br style=3D"font-family: Helvetica; font-siz=
e: 12px; font-style: normal; font-variant: normal; font-weight: normal; let=
ter-spacing: normal; line-height: normal; orphans: auto; text-align: start;=
text-indent: 0px; text-transform: none; white-space: normal; widows: auto;=
word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=3D""></div></blo=
ckquote>It technically does, but it=E2=80=99s very verbose. I=E2=80=99d rea=
lly like something that=E2=80=99s a bit more user friendly.<br class=3D""><=
br class=3D""><blockquote type=3D"cite" class=3D""><div class=3D""><blockqu=
ote type=3D"cite" style=3D"font-family: Helvetica; font-size: 12px; font-st=
yle: normal; font-variant: normal; font-weight: normal; letter-spacing: nor=
mal; line-height: normal; orphans: auto; text-align: start; text-indent: 0p=
x; text-transform: none; white-space: normal; widows: auto; word-spacing: 0=
px; -webkit-text-stroke-width: 0px;" class=3D""><br class=3D""></blockquote=
><span style=3D"font-family: Helvetica; font-size: 12px; font-style: normal=
; font-variant: normal; font-weight: normal; letter-spacing: normal; line-h=
eight: normal; orphans: auto; text-align: start; text-indent: 0px; text-tra=
nsform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit=
-text-stroke-width: 0px; float: none; display: inline !important;" class=3D=
"">Why do you deduce that the property must be copied? Shouldn't T deduce t=
o X&.</span><br style=3D"font-family: Helvetica; font-size: 12px; font-=
style: normal; font-variant: normal; font-weight: normal; letter-spacing: n=
ormal; line-height: normal; orphans: auto; text-align: start; text-indent: =
0px; text-transform: none; white-space: normal; widows: auto; word-spacing:=
0px; -webkit-text-stroke-width: 0px;" class=3D""><span style=3D"font-famil=
y: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; fo=
nt-weight: normal; letter-spacing: normal; line-height: normal; orphans: au=
to; text-align: start; text-indent: 0px; text-transform: none; white-space:=
normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; f=
loat: none; display: inline !important;" class=3D"">However</span><br style=
=3D"font-family: Helvetica; font-size: 12px; font-style: normal; font-varia=
nt: normal; font-weight: normal; letter-spacing: normal; line-height: norma=
l; orphans: auto; text-align: start; text-indent: 0px; text-transform: none=
; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke=
-width: 0px;" class=3D""><span style=3D"font-family: Helvetica; font-size: =
12px; font-style: normal; font-variant: normal; font-weight: normal; letter=
-spacing: normal; line-height: normal; orphans: auto; text-align: start; te=
xt-indent: 0px; text-transform: none; white-space: normal; widows: auto; wo=
rd-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inli=
ne !important;" class=3D"">void foo(A::X);</span><br style=3D"font-family: =
Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-=
weight: normal; letter-spacing: normal; line-height: normal; orphans: auto;=
text-align: start; text-indent: 0px; text-transform: none; white-space: no=
rmal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" cla=
ss=3D""><br style=3D"font-family: Helvetica; font-size: 12px; font-style: n=
ormal; font-variant: normal; font-weight: normal; letter-spacing: normal; l=
ine-height: normal; orphans: auto; text-align: start; text-indent: 0px; tex=
t-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -w=
ebkit-text-stroke-width: 0px;" class=3D""><span style=3D"font-family: Helve=
tica; font-size: 12px; font-style: normal; font-variant: normal; font-weigh=
t: normal; letter-spacing: normal; line-height: normal; orphans: auto; text=
-align: start; text-indent: 0px; text-transform: none; white-space: normal;=
widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: no=
ne; display: inline !important;" class=3D"">must be ill formed.</span><br s=
tyle=3D"font-family: Helvetica; font-size: 12px; font-style: normal; font-v=
ariant: normal; font-weight: normal; letter-spacing: normal; line-height: n=
ormal; orphans: auto; text-align: start; text-indent: 0px; text-transform: =
none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-st=
roke-width: 0px;" class=3D""></div></blockquote><div>Template parameters do=
n=E2=80=99t deduce to references. You need to explicitly write foo(T&) =
if you want a lvalue reference parameter.</div><div><br class=3D""></div><b=
lockquote type=3D"cite" class=3D""><div class=3D""><blockquote type=3D"cite=
" style=3D"font-family: Helvetica; font-size: 12px; font-style: normal; fon=
t-variant: normal; font-weight: normal; letter-spacing: normal; line-height=
: normal; orphans: auto; text-align: start; text-indent: 0px; text-transfor=
m: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text=
-stroke-width: 0px;" class=3D"">foo(a.y); // also ill-formed but shouldn't =
be: the intention is to pass a.y's value<br class=3D""><br class=3D"">A pos=
sible way of clearing this ambiguity is with a contextual(?) keyword<br cla=
ss=3D"">class A {<br class=3D""> // x is not supposed to be a property=
<br class=3D""> inline class X { ... } x;<br class=3D""> // y is =
supposed to be a property<br class=3D""> // "property int" replaces th=
e "inline class..." boilerplate<br class=3D""> property int y { /* def=
ault implementation get/set auto-generated where not user-provided */ };<br=
class=3D""></blockquote><span style=3D"font-family: Helvetica; font-size: =
12px; font-style: normal; font-variant: normal; font-weight: normal; letter=
-spacing: normal; line-height: normal; orphans: auto; text-align: start; te=
xt-indent: 0px; text-transform: none; white-space: normal; widows: auto; wo=
rd-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inli=
ne !important;" class=3D"">I don't see the advantage of having properties t=
hat behave as raw data member, but this is out of the scope of the thread.<=
/span><br style=3D"font-family: Helvetica; font-size: 12px; font-style: nor=
mal; font-variant: normal; font-weight: normal; letter-spacing: normal; lin=
e-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-=
transform: none; white-space: normal; widows: auto; word-spacing: 0px; -web=
kit-text-stroke-width: 0px;" class=3D""></div></blockquote>I often see prop=
erties where only the setter is user-defined and the getter is left unchang=
ed. It has its uses. But these languages (C#, Obj-C) also have some syntax/=
convention to assign to the actual backing variable (which the auto-generat=
ed getter is reading) from within the setter. If we don=E2=80=99t get that =
the usefulness drops significantly.<br class=3D""><blockquote type=3D"cite"=
class=3D""><div class=3D""><blockquote type=3D"cite" style=3D"font-family:=
Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font=
-weight: normal; letter-spacing: normal; line-height: normal; orphans: auto=
; text-align: start; text-indent: 0px; text-transform: none; white-space: n=
ormal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" cl=
ass=3D"">};<br class=3D"">template<class T> void foo(T);<br class=3D"=
">A a;<br class=3D"">foo(a.x); // ill-formed, cannot copy inline class<br c=
lass=3D"">foo(a.y); // instantiation ill-formed at first, but it's a proper=
ty so try again with the implicit int conversion<br class=3D""><br class=3D=
"">When it comes to references, then as already stated the property type is=
expected to be implicitly convertible to/assignable from an instance of it=
s underlying type. So even if argument deduction were to prefer the inline =
class's type, the property still behaves under duck typing as-if it were th=
e value. Concretely in the following code:<br class=3D""><br class=3D"">tem=
palte<class T><br class=3D"">void foo(T& prop) { prop =3D 5; }<br=
class=3D""><br class=3D"">It should be irrelevant whether T deduces to "in=
t" or "MyHealthProperty<Player, 32>".<br class=3D""><br class=3D"">Bu=
t it is more difficult with pointers. Whereas you can assign an int& to=
a MyHealthProperty<Player, 32>& you cannot assign a int* to a My=
HealthProperty<Player, 32>*. Therefore it makes a pretty big differen=
ce whether one deduces int* or MyHealthProperty<Player, 32>*. But tak=
ing the address of the result of a property's getter is only possible if it=
doesn't return an rvalue so this remains an open problem.<br class=3D""><b=
r class=3D""><br class=3D""><br class=3D"">Well that took longer than expec=
ted. And now I realize that this still overlaps with the properties thread.=
I guess that won't change unless it is decided that inline classes are not=
the right tool to implement them. Sigh.<br class=3D""><br class=3D""></blo=
ckquote><span style=3D"font-family: Helvetica; font-size: 12px; font-style:=
normal; font-variant: normal; font-weight: normal; letter-spacing: normal;=
line-height: normal; orphans: auto; text-align: start; text-indent: 0px; t=
ext-transform: none; white-space: normal; widows: auto; word-spacing: 0px; =
-webkit-text-stroke-width: 0px; float: none; display: inline !important;" c=
lass=3D"">Thanks for the long post.I see two major issues up to now:</span>=
<br style=3D"font-family: Helvetica; font-size: 12px; font-style: normal; f=
ont-variant: normal; font-weight: normal; letter-spacing: normal; line-heig=
ht: normal; orphans: auto; text-align: start; text-indent: 0px; text-transf=
orm: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-te=
xt-stroke-width: 0px;" class=3D""><span style=3D"font-family: Helvetica; fo=
nt-size: 12px; font-style: normal; font-variant: normal; font-weight: norma=
l; letter-spacing: normal; line-height: normal; orphans: auto; text-align: =
start; text-indent: 0px; text-transform: none; white-space: normal; widows:=
auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; disp=
lay: inline !important;" class=3D"">* managing with several instances of th=
e same inline class implies either having different types or including a ru=
n-time overhead.</span><br style=3D"font-family: Helvetica; font-size: 12px=
; font-style: normal; font-variant: normal; font-weight: normal; letter-spa=
cing: normal; line-height: normal; orphans: auto; text-align: start; text-i=
ndent: 0px; text-transform: none; white-space: normal; widows: auto; word-s=
pacing: 0px; -webkit-text-stroke-width: 0px;" class=3D""><span style=3D"fon=
t-family: Helvetica; font-size: 12px; font-style: normal; font-variant: nor=
mal; font-weight: normal; letter-spacing: normal; line-height: normal; orph=
ans: auto; text-align: start; text-indent: 0px; text-transform: none; white=
-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width:=
0px; float: none; display: inline !important;" class=3D"">* passing inline=
class instances by reference transforms any such function in a template if=
the OFFSET is part of the type.</span><br style=3D"font-family: Helvetica;=
font-size: 12px; font-style: normal; font-variant: normal; font-weight: no=
rmal; letter-spacing: normal; line-height: normal; orphans: auto; text-alig=
n: start; text-indent: 0px; text-transform: none; white-space: normal; wido=
ws: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=3D""><b=
r style=3D"font-family: Helvetica; font-size: 12px; font-style: normal; fon=
t-variant: normal; font-weight: normal; letter-spacing: normal; line-height=
: normal; orphans: auto; text-align: start; text-indent: 0px; text-transfor=
m: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text=
-stroke-width: 0px;" class=3D""><span style=3D"font-family: Helvetica; font=
-size: 12px; font-style: normal; font-variant: normal; font-weight: normal;=
letter-spacing: normal; line-height: normal; orphans: auto; text-align: st=
art; text-indent: 0px; text-transform: none; white-space: normal; widows: a=
uto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; displa=
y: inline !important;" class=3D"">Storing the offset on the inline class so=
lves both issues, but introduce a new one :(</span><br style=3D"font-family=
: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; fon=
t-weight: normal; letter-spacing: normal; line-height: normal; orphans: aut=
o; text-align: start; text-indent: 0px; text-transform: none; white-space: =
normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" c=
lass=3D""><br style=3D"font-family: Helvetica; font-size: 12px; font-style:=
normal; font-variant: normal; font-weight: normal; letter-spacing: normal;=
line-height: normal; orphans: auto; text-align: start; text-indent: 0px; t=
ext-transform: none; white-space: normal; widows: auto; word-spacing: 0px; =
-webkit-text-stroke-width: 0px;" class=3D""><br style=3D"font-family: Helve=
tica; font-size: 12px; font-style: normal; font-variant: normal; font-weigh=
t: normal; letter-spacing: normal; line-height: normal; orphans: auto; text=
-align: start; text-indent: 0px; text-transform: none; white-space: normal;=
widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=3D=
""><span style=3D"font-family: Helvetica; font-size: 12px; font-style: norm=
al; font-variant: normal; font-weight: normal; letter-spacing: normal; line=
-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-t=
ransform: none; white-space: normal; widows: auto; word-spacing: 0px; -webk=
it-text-stroke-width: 0px; float: none; display: inline !important;" class=
=3D"">Vicente</span><br style=3D"font-family: Helvetica; font-size: 12px; f=
ont-style: normal; font-variant: normal; font-weight: normal; letter-spacin=
g: normal; line-height: normal; orphans: auto; text-align: start; text-inde=
nt: 0px; text-transform: none; white-space: normal; widows: auto; word-spac=
ing: 0px; -webkit-text-stroke-width: 0px;" class=3D""><br style=3D"font-fam=
ily: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; =
font-weight: normal; letter-spacing: normal; line-height: normal; orphans: =
auto; text-align: start; text-indent: 0px; text-transform: none; white-spac=
e: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;=
" class=3D""><span style=3D"font-family: Helvetica; font-size: 12px; font-s=
tyle: normal; font-variant: normal; font-weight: normal; letter-spacing: no=
rmal; line-height: normal; orphans: auto; text-align: start; text-indent: 0=
px; text-transform: none; white-space: normal; widows: auto; word-spacing: =
0px; -webkit-text-stroke-width: 0px; float: none; display: inline !importan=
t;" class=3D"">--<span class=3D"Apple-converted-space"> </span></span>=
<br style=3D"font-family: Helvetica; font-size: 12px; font-style: normal; f=
ont-variant: normal; font-weight: normal; letter-spacing: normal; line-heig=
ht: normal; orphans: auto; text-align: start; text-indent: 0px; text-transf=
orm: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-te=
xt-stroke-width: 0px;" class=3D""><br style=3D"font-family: Helvetica; font=
-size: 12px; font-style: normal; font-variant: normal; font-weight: normal;=
letter-spacing: normal; line-height: normal; orphans: auto; text-align: st=
art; text-indent: 0px; text-transform: none; white-space: normal; widows: a=
uto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=3D""><span s=
tyle=3D"font-family: Helvetica; font-size: 12px; font-style: normal; font-v=
ariant: normal; font-weight: normal; letter-spacing: normal; line-height: n=
ormal; orphans: auto; text-align: start; text-indent: 0px; text-transform: =
none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-st=
roke-width: 0px; float: none; display: inline !important;" class=3D"">--- Y=
ou received this message because you are subscribed to the Google Groups "I=
SO C++ Standard - Future Proposals" group.</span><br style=3D"font-family: =
Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-=
weight: normal; letter-spacing: normal; line-height: normal; orphans: auto;=
text-align: start; text-indent: 0px; text-transform: none; white-space: no=
rmal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" cla=
ss=3D""><span style=3D"font-family: Helvetica; font-size: 12px; font-style:=
normal; font-variant: normal; font-weight: normal; letter-spacing: normal;=
line-height: normal; orphans: auto; text-align: start; text-indent: 0px; t=
ext-transform: none; white-space: normal; widows: auto; word-spacing: 0px; =
-webkit-text-stroke-width: 0px; float: none; display: inline !important;" c=
lass=3D"">To unsubscribe from this group and stop receiving emails from it,=
send an email to<span class=3D"Apple-converted-space"> </span></span>=
<a href=3D"mailto:std-proposals+unsubscribe@isocpp.org" style=3D"font-famil=
y: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; fo=
nt-weight: normal; letter-spacing: normal; line-height: normal; orphans: au=
to; text-align: start; text-indent: 0px; text-transform: none; white-space:=
normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" =
class=3D"">std-proposals+unsubscribe@isocpp.org</a><span style=3D"font-fami=
ly: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; f=
ont-weight: normal; letter-spacing: normal; line-height: normal; orphans: a=
uto; text-align: start; text-indent: 0px; text-transform: none; white-space=
: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
float: none; display: inline !important;" class=3D"">.</span><br style=3D"f=
ont-family: Helvetica; font-size: 12px; font-style: normal; font-variant: n=
ormal; font-weight: normal; letter-spacing: normal; line-height: normal; or=
phans: auto; text-align: start; text-indent: 0px; text-transform: none; whi=
te-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-widt=
h: 0px;" class=3D""><span style=3D"font-family: Helvetica; font-size: 12px;=
font-style: normal; font-variant: normal; font-weight: normal; letter-spac=
ing: normal; line-height: normal; orphans: auto; text-align: start; text-in=
dent: 0px; text-transform: none; white-space: normal; widows: auto; word-sp=
acing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !i=
mportant;" class=3D"">To post to this group, send email to<span class=3D"Ap=
ple-converted-space"> </span></span><a href=3D"mailto:std-proposals@is=
ocpp.org" style=3D"font-family: Helvetica; font-size: 12px; font-style: nor=
mal; font-variant: normal; font-weight: normal; letter-spacing: normal; lin=
e-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-=
transform: none; white-space: normal; widows: auto; word-spacing: 0px; -web=
kit-text-stroke-width: 0px;" class=3D"">std-proposals@isocpp.org</a><span s=
tyle=3D"font-family: Helvetica; font-size: 12px; font-style: normal; font-v=
ariant: normal; font-weight: normal; letter-spacing: normal; line-height: n=
ormal; orphans: auto; text-align: start; text-indent: 0px; text-transform: =
none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-st=
roke-width: 0px; float: none; display: inline !important;" class=3D"">.</sp=
an><br style=3D"font-family: Helvetica; font-size: 12px; font-style: normal=
; font-variant: normal; font-weight: normal; letter-spacing: normal; line-h=
eight: normal; orphans: auto; text-align: start; text-indent: 0px; text-tra=
nsform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit=
-text-stroke-width: 0px;" class=3D""><span style=3D"font-family: Helvetica;=
font-size: 12px; font-style: normal; font-variant: normal; font-weight: no=
rmal; letter-spacing: normal; line-height: normal; orphans: auto; text-alig=
n: start; text-indent: 0px; text-transform: none; white-space: normal; wido=
ws: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; d=
isplay: inline !important;" class=3D"">Visit this group at<span class=3D"Ap=
ple-converted-space"> </span></span><a href=3D"http://groups.google.co=
m/a/isocpp.org/group/std-proposals/" style=3D"font-family: Helvetica; font-=
size: 12px; font-style: normal; font-variant: normal; font-weight: normal; =
letter-spacing: normal; line-height: normal; orphans: auto; text-align: sta=
rt; text-indent: 0px; text-transform: none; white-space: normal; widows: au=
to; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=3D"">http://g=
roups.google.com/a/isocpp.org/group/std-proposals/</a><span style=3D"font-f=
amily: Helvetica; font-size: 12px; font-style: normal; font-variant: normal=
; font-weight: normal; letter-spacing: normal; line-height: normal; orphans=
: auto; text-align: start; text-indent: 0px; text-transform: none; white-sp=
ace: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0p=
x; float: none; display: inline !important;" class=3D"">.</span></div></blo=
ckquote></div><br class=3D""></body></html>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--Apple-Mail=_DB83D42B-278F-4D12-AED1-46FDD97560A9--
.
Author: "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr>
Date: Tue, 1 Sep 2015 20:46:36 +0200
Raw View
This is a multi-part message in MIME format.
--------------040201030303080606020601
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: quoted-printable
Le 01/09/15 20:33, Matthew Woehlke a =C3=A9crit :
> On 2015-09-01 14:20, Vicente J. Botet Escriba wrote:
>> Le 01/09/15 20:02, Matthew Woehlke a =C3=A9crit :
>>> Conversely, trying to maintain the utility of inline classes *without*
>>> the zero-storage guarantee makes them significantly more complicated to
>>> implement.
>> Could you explain why?
> I think I've addressed this sufficiently already that I'm not going to
> repeat myself. The main point is that being zero-sized *is*, IMO, one of
> the primary (if not *the*) motivating features. Once you allow them to
> maybe-or-maybe-not be zero sized, you have to deal with all sorts of
> special cases depending whether they are or not, what happens when you
> have more than one instance, and so forth. In short, you either
> sacrifice a major rationale or make the proposal much, much more
> complicated.
>
How can you ensure that an inline class has size 0 if has members?
If you can not, how having the possibility that they have size 0 when=20
they have no members simplifies the proposal?
Vicente
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
--------------040201030303080606020601
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<html>
<head>
<meta content=3D"text/html; charset=3Dutf-8" http-equiv=3D"Content-Type=
">
</head>
<body text=3D"#000000" bgcolor=3D"#FFFFFF">
<div class=3D"moz-cite-prefix">Le 01/09/15 20:33, Matthew Woehlke a
=C3=A9crit=C2=A0:<br>
</div>
<blockquote cite=3D"mid:ms4r25$le4$2@ger.gmane.org" type=3D"cite">
<pre wrap=3D"">On 2015-09-01 14:20, Vicente J. Botet Escriba wrote:
</pre>
<blockquote type=3D"cite">
<pre wrap=3D"">Le 01/09/15 20:02, Matthew Woehlke a =C3=A9crit :
</pre>
<blockquote type=3D"cite">
<pre wrap=3D"">Conversely, trying to maintain the utility of inli=
ne classes *without*
the zero-storage guarantee makes them significantly more complicated to
implement.
</pre>
</blockquote>
<pre wrap=3D"">
Could you explain why?
</pre>
</blockquote>
<pre wrap=3D"">
I think I've addressed this sufficiently already that I'm not going to
repeat myself. The main point is that being zero-sized *is*, IMO, one of
the primary (if not *the*) motivating features. Once you allow them to
maybe-or-maybe-not be zero sized, you have to deal with all sorts of
special cases depending whether they are or not, what happens when you
have more than one instance, and so forth. In short, you either
sacrifice a major rationale or make the proposal much, much more
complicated.
</pre>
</blockquote>
<font size=3D"+1">How can you ensure that an inline class has size 0
if has members?<br>
If you can not, how having the possibility that they have size 0
when they have no members simplifies the proposal?<br>
<br>
Vicente<br>
</font>
</body>
</html>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--------------040201030303080606020601--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Tue, 01 Sep 2015 14:54:11 -0400
Raw View
On 2015-09-01 14:46, Vicente J. Botet Escriba wrote:
> How can you ensure that an inline class has size 0 if has members?
I don't. I've said, repeatedly, that it's *not allowed* to have
(non-static data) members.
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Tue, 1 Sep 2015 11:56:34 -0700 (PDT)
Raw View
------=_Part_5046_1630244769.1441133794674
Content-Type: multipart/alternative;
boundary="----=_Part_5047_1427855174.1441133794675"
------=_Part_5047_1427855174.1441133794675
Content-Type: text/plain; charset=UTF-8
On Tuesday, September 1, 2015 at 2:28:52 PM UTC-4, Matthew Woehlke wrote:
>
> On 2015-08-31 15:25, Bjorn Reese wrote:
> > I frequently come across use-cases where inline classes can be useful.
> >
> > One use-case is to compensate for the lack of partial template
> > specialization of class member functions. A workaround is to introduce
> > a functor with static member functions, which are passed the class
> > instance as a parameter, because the functor needs to operate on the
> > class member variables. For example:
> >
> > class value
> > {
> > public:
> > template <typename T>
> > T convert() const;
> > };
> >
> > Say that need a specialization for value::convert<T>() for integral
> > types. This can be done via the following functor:
> >
> > template <typename T, typename Enable = void>
> > struct value_functor {};
> >
> > template <typename T>
> > struct value_functor<T, enable_if<is_integral<T>>::type>
> > {
> > static T convert(const value& v)
> > {
> > // Do integral conversion here
> > }
> > };
> >
> > This is invoked as follows:
> >
> > template <typename T>
> > T value::convert() const
> > {
> > return value_functor<ReturnType>::convert(*this);
> > }
> >
> > Furthermore, if value_functor must access private members in the value
> > class, then it must become a friend of the class.
> >
> > An inline class, as I have understood it, will handle the above case
> > more elegantly.
>
> Do you mean like this?
>
> class Value
> {
> public:
> template <typename T> inline class Conversion
> {
> // ...generic implementation...
> }
>
> template <typename T> Conversion<T> convert;
> };
>
> template <typename T>
> inline class value::conversion<T, enable_if<is_integral<T>>::type>
> {
> T operator()() const { /* access members of Value here */ }
> };
>
> Value v;
> long x = v.convert<long>();
>
> --
> Matthew
>
Yes, that's something like what I was looking for. And it's... weird.
Specifically, the definition of `Value::convert`. I'm not sure I understand
what that's doing.
Value::convert is a non-static data member. And those can't be variable
templates, as far as I know. So... how does that work? Besides you saying
"inline class members can be variable templates".
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_5047_1427855174.1441133794675
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Tuesday, September 1, 2015 at 2:28:52 PM UTC-4, Matthew Woehlke wrote:<b=
lockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;borde=
r-left: 1px #ccc solid;padding-left: 1ex;">On 2015-08-31 15:25, Bjorn Reese=
wrote:
<br>> I frequently come across use-cases where inline classes can be use=
ful.
<br>>=20
<br>> One use-case is to compensate for the lack of partial template
<br>> specialization of class member functions. A workaround is to intro=
duce
<br>> a functor with static member functions, which are passed the class
<br>> instance as a parameter, because the functor needs to operate on t=
he
<br>> class member variables. For example:
<br>>=20
<br>> =C2=A0 class value
<br>> =C2=A0 {
<br>> =C2=A0 public:
<br>> =C2=A0 =C2=A0 template <typename T>
<br>> =C2=A0 =C2=A0 T convert() const;
<br>> =C2=A0 };
<br>>=20
<br>> Say that need a specialization for value::convert<T>() for i=
ntegral
<br>> types. This can be done via the following functor:
<br>>=20
<br>> =C2=A0 template <typename T, typename Enable =3D void>
<br>> =C2=A0 struct value_functor {};
<br>>=20
<br>> =C2=A0 template <typename T>
<br>> =C2=A0 struct value_functor<T, enable_if<is_integral<T>=
;>::<wbr>type>
<br>> =C2=A0 {
<br>> =C2=A0 =C2=A0 static T convert(const value& v)
<br>> =C2=A0 =C2=A0 {
<br>> =C2=A0 =C2=A0 =C2=A0 =C2=A0// Do integral conversion here
<br>> =C2=A0 =C2=A0 }
<br>> =C2=A0 };
<br>>=20
<br>> This is invoked as follows:
<br>>=20
<br>> =C2=A0 template <typename T>
<br>> =C2=A0 T value::convert() const
<br>> =C2=A0 {
<br>> =C2=A0 =C2=A0 return value_functor<ReturnType>::<wbr>convert=
(*this);
<br>> =C2=A0 }
<br>>=20
<br>> Furthermore, if value_functor must access private members in the v=
alue
<br>> class, then it must become a friend of the class.
<br>>=20
<br>> An inline class, as I have understood it, will handle the above ca=
se
<br>> more elegantly.
<br>
<br>Do you mean like this?
<br>
<br>=C2=A0 class Value
<br>=C2=A0 {
<br>=C2=A0 public:
<br>=C2=A0 =C2=A0 template <typename T> inline class Conversion
<br>=C2=A0 =C2=A0 {
<br>=C2=A0 =C2=A0 =C2=A0 // ...generic implementation...
<br>=C2=A0 =C2=A0 }
<br>
<br>=C2=A0 =C2=A0 template <typename T> Conversion<T> convert;
<br>=C2=A0 };
<br>
<br>=C2=A0 template <typename T>
<br>=C2=A0 inline class value::conversion<T, enable_if<is_integral<=
;T>>::<wbr>type>
<br>=C2=A0 {
<br>=C2=A0 =C2=A0 T operator()() const { /* access members of Value here */=
}
<br>=C2=A0 };
<br>
<br>=C2=A0 Value v;
<br>=C2=A0 long x =3D v.convert<long>();
<br>
<br>--=20
<br>Matthew
<br></blockquote><div><br>Yes, that's something like what I was looking=
for. And it's... weird. Specifically, the definition of `Value::conver=
t`. I'm not sure I understand what that's doing.<br><br>Value::conv=
ert is a non-static data member. And those can't be variable templates,=
as far as I know. So... how does that work? Besides you saying "inlin=
e class members can be variable templates".<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_5047_1427855174.1441133794675--
------=_Part_5046_1630244769.1441133794674--
.
Author: "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr>
Date: Tue, 1 Sep 2015 21:07:18 +0200
Raw View
This is a multi-part message in MIME format.
--------------000500090200000501060203
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: quoted-printable
Le 01/09/15 20:54, Matthew Woehlke a =C3=A9crit :
> On 2015-09-01 14:46, Vicente J. Botet Escriba wrote:
>> How can you ensure that an inline class has size 0 if has members?
> I don't. I've said, repeatedly, that it's *not allowed* to have
> (non-static data) members.
>
Sorry, I missed it. I don't know if the property example tis your, but=20
this example has data members. If inline classes can not contain=20
non-static data members I'm less interested in this feature as I will be=20
unable to use them for subjects/services :(
Vicente
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
--------------000500090200000501060203
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<html>
<head>
<meta content=3D"text/html; charset=3Dutf-8" http-equiv=3D"Content-Type=
">
</head>
<body text=3D"#000000" bgcolor=3D"#FFFFFF">
<div class=3D"moz-cite-prefix">Le 01/09/15 20:54, Matthew Woehlke a
=C3=A9crit=C2=A0:<br>
</div>
<blockquote cite=3D"mid:ms4s8o$ept$1@ger.gmane.org" type=3D"cite">
<pre wrap=3D"">On 2015-09-01 14:46, Vicente J. Botet Escriba wrote:
</pre>
<blockquote type=3D"cite">
<pre wrap=3D"">How can you ensure that an inline class has size 0 i=
f has members?
</pre>
</blockquote>
<pre wrap=3D"">
I don't. I've said, repeatedly, that it's *not allowed* to have
(non-static data) members.
</pre>
</blockquote>
<font size=3D"+1">Sorry, I missed it. I don't know if the property
example tis your, but this example has data members. If inline
classes can not contain non-static data members I'm less
interested in this feature as I will be unable to use them for
subjects/services :(<br>
<br>
Vicente<br>
</font>
</body>
</html>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--------------000500090200000501060203--
.
Author: "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr>
Date: Tue, 1 Sep 2015 21:08:25 +0200
Raw View
Le 01/09/15 20:40, Miro Knejp a =C3=A9crit :
>> On 01 Sep 2015, at 20:15 , Vicente J. Botet Escriba <vicente.botet@wanad=
oo.fr> wrote:
>>
>> I don't see the trouble copying inline class instances between two outer=
class instances if they are of the good type.
> Yes, that=E2=80=99s fine.
>>> Con:
>>> * If empty inline subobjects have no storage then multiple instances ca=
n have the same address.
>> I believe that this is orthogonal and related to the possibility of havi=
ng object of size 0, which should be taken by another proposal.
>>> * In the context of properties (under the assumption they are implement=
ed with this technique) it is potentially a leaking abstraction. Other lang=
uages don't allow you to pass around the actual property itself, only the r=
esult of their getter. But then, these languages usually don't have the con=
cept of references. It might be an interesting tool to pass a property by-r=
eference to a function and have that function operate on the property inste=
ad of only its value. But it poses the ambiguity of when to deduce the prop=
erty object and when the property's value.
>> I don't seethe problem. please could you clarify?
> If you have a function template<class T> void foo(const T&) then the dedu=
ction results in the inline class=E2=80=99s type for T. However if T repres=
ents a property then you have now exposed the property implementation detai=
l. I don=E2=80=99t know if this is really a problem, but some have argued i=
t is a leaky abstraction and thus undesireable.
I'm missing how this expose any implementation detail?
>>> *(4)* Should empty inline subobjects have storage?
>> Lets say no for the time being.
>>> Pro: zero overhead
>>> Con: see (3)
>>>
>>> *(5)* Should separate inline subobjects be allowed to have the same typ=
e?
>> yes.
>>> Pro: It's convenient.
>>> Con:
>>> * If they aren't empty and access their state in member functions the c=
ompiler must find a way to distinguish them. Either by storing a hidden off=
set or by instantiating different actual types. Both options aren't zero ov=
erhead either in object size or code size.
>> Right.
>>> * If every inline subobject has a different type and (3) is resolved as=
yes then it is possible to safely static_cast between the containing insta=
nce and the inline subobject.
>>>
>> I don't understand this cons.
> It allows you to do
> A a;
> auto& x =3D static_cast<A::X&>(a);
Why would you want to allow this?
> auto& y =3D static_cast<A::Y&>(a);
> auto& a2 =3D static_cast<A&>(x);
> auto& a3 =3D static_cast<A&>(y);
>
> But only if A.x and A.y have different types. If they have the same type =
it only works if a.x and a.y somehow know their offset inside A.
>
>> <snip>
>>>
>>> *(5)* My answer here is no, there cannot be two inline subobjects of th=
e same type.
>>>
>>> The problem:
>>> class B {
>>> P x, y;
>>> void baz();
>>> };
>>> B b;
>>> auto& x =3D b.x;
>>> auto& y =3D b.y;
>>> x.get();
>>> y.get();
>>> This poses a conflict because x and y have different offsets. The compi=
ler has to either store the offset inside b.x and b.y or generate different=
types for x and y with different get/set implementations. The latter resul=
ts in references to B::x and B::y to be incompatible, which is surprising b=
ecause from the code they seem to have the same.
>> You are right that they are incompatible in the current standard if diff=
erent types are generated. We need to find a specific solution for inline c=
lasses.
>>> My proposed solution is to introduce a new type for each inline class i=
f you try to re-use some shared inline type.
>>> class C {
>>> inline class X : public P { } x; // Instantiate P<C, 0>
>>> inline class Y : public P { } y; // Instantiate P<C, 4>
>>> inline class Z {
>>> // ...
>>> } z1, z2; // ill-formed: only one instance per type
>>> void baz();
>>> };
>>> C c;
>>> auto& x =3D c.x;
>>> auto& y =3D c.y;
>>> x.get();
>>> y.get();
>>> Now there is no longer an ambiguity and it is clearly evident from the =
fact that X and Y are distinct types in the code.
>> IMHO, this doesn't solves the problem.
> It technically does, but it=E2=80=99s very verbose. I=E2=80=99d really li=
ke something that=E2=80=99s a bit more user friendly.
I have not understood the problem then.
>
>> Why do you deduce that the property must be copied? Shouldn't T deduce t=
o X&.
>> However
>> void foo(A::X);
>>
>> must be ill formed.
> Template parameters don=E2=80=99t deduce to references. You need to expli=
citly write foo(T&) if you want a lvalue reference parameter.
Then it must be ill formed. If the user wants the conversion s/he would=20
need to explicitly state it.
>
>>> foo(a.y); // also ill-formed but shouldn't be: the intention is to pass=
a.y's value
>>>
>>> A possible way of clearing this ambiguity is with a contextual(?) keywo=
rd
>>> class A {
>>> // x is not supposed to be a property
>>> inline class X { ... } x;
>>> // y is supposed to be a property
>>> // "property int" replaces the "inline class..." boilerplate
>>> property int y { /* default implementation get/set auto-generated whe=
re not user-provided */ };
>> I don't see the advantage of having properties that behave as raw data m=
ember, but this is out of the scope of the thread.
> I often see properties where only the setter is user-defined and the gett=
er is left unchanged. It has its uses. But these languages (C#, Obj-C) also=
have some syntax/convention to assign to the actual backing variable (whic=
h the auto-generated getter is reading) from within the setter. If we don=
=E2=80=99t get that the usefulness drops significantly.
As I said this is out of the scope of this thread. inline classes could=20
be used to implement properties, but the syntax wouldn't be as friendly=20
as a specific property feature.
Vicente
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Tue, 01 Sep 2015 15:12:05 -0400
Raw View
On 2015-09-01 14:06, Miro Knejp wrote:
> On 01 Sep 2015, at 19:45 , Matthew Woehlke wrote:
>> I disagree that this should magically become well-formed when you have a
>> member of type P inside some class that has a 'baz'. What I was trying
>> to say is that I... am not comfortable with your suggestion that the
>> above is magically a CRTP template. If you want CRTP-like behavior...
>> use CRTP.
>
> If you have this->foo() in a class with a dependent base class it
> also magically becomes well-formed when instantiated.
Not really. The difference here is that you *have* a base class. And a
template. Your example had neither.
> If we assume name lookup in inline classes works as-if they were=20
> derived from the parent, this is one consequence of that.
In the example you gave, your class doesn't *have* a parent. The "as if"
is for the class *definitions*, not the instantiations. (It can't be for
the instantiations, because that would imply that the compiler generates
new code for each instantiation. Which, okay, I get that you're asking
for that anyway, but that's not what I intended.)
I intended that code generation for inline classes is the same as for
traditional classes; that is, only one instance of the code for a
non-template inline class.
> Even if we omit the two-phase lookup thing, the compiler
> still has to do *some* CRTP magic for non-empty inline classes.
Can you elaborate?
> Even if only empty types are considered to begin with we can at least
> anticipate non-empty inline classes in the future and not
> artificially restrict our future selves.
I still disagree with making things that don't look like templates
suddenly behave like templates any more than necessary. It's one thing
for multiple instances of the same type to become implicitly templates
because of the offset magic needed to make their data members distinct.
It's quite another thing for a freestanding class *that isn't a
template* (at least, doesn't *look* like one) to turn into a template
(i.e. not have its code compiled until it is actually instantiated).
>> On 2015-09-01 12:59, Miro Knejp wrote:
>>> On 01 Sep 2015, at 16:56 , Matthew Woehlke wrote:
>>>> Repeating myself a bit here, but I see two possibilities: either the
>>>> callee wants a mutable reference, and so the only way to make the call
>>>> is to pass a reference to the property anyway, or else it takes a cons=
t
>>>> reference and passing the property reference could anyway only change
>>>> the program semantics if the callee uses the value multiple times, and
>>>> the value changes in the mean time.
>>>
>>> You have the same problems with regular references already. The value
>>> of a reference you hold could change between accesses.
>>
>> True, but in the non-property case, a correctly designed API won't ever
>> give you a reference.
>
> A correctly designed API won=E2=80=99t do many things. If it=E2=80=99s no=
t ill-formed
> it needs to be specified.
I don't understand where there is a problem here. If some method takes
an int&, and I pass a property with a conversion to int&, such code can
be compiled. What happens if that int changes is no more or less
specified than if I passed an int& directly. Addressing that is
completely out of scope.
If that's not what you're talking about, please restate the problem,
because I am confused...
--=20
Matthew
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Tue, 1 Sep 2015 12:16:39 -0700 (PDT)
Raw View
------=_Part_8_1078575196.1441134999175
Content-Type: multipart/alternative;
boundary="----=_Part_9_1625376422.1441134999175"
------=_Part_9_1625376422.1441134999175
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Tuesday, September 1, 2015 at 2:02:49 PM UTC-4, Matthew Woehlke wrote:
>
> On 2015-08-30 15:13, Klaim - Jo=C3=ABl Lamotte wrote:=20
> > My suggestion was to discuss this outside the properties thread because=
=20
> > it's impossible to follow with the several sub-discussions ongoing.=20
>
> Also per Nicol's request, I'm attaching=C2=B9 my current draft. This isn'=
t=20
> exactly formal proposal yet (in particular, there is no standardese at=20
> all), but I'm trying to at least start in the direction of a more formal=
=20
> paper rather than just paragraphs in an e-mail.=20
>
> The "zero storage object" rationale is... "lacking", since I'm less=20
> familiar with that one. I'd appreciate help filling that out.=20
>
> Comments encouraged, of course. Also, please remind me what bits I=20
> forgot about, as I'm sure there are some.=20
>
> (=C2=B9 I hope to get this on github eventually, but I don't have the abi=
lity=20
> to access my account at the moment.)=20
>
First, thank you for writing a concrete proposal. And more importantly,=20
thank you for formatting it in plain text. Lots of people are too in live=
=20
with throwing PDFs around these days...
Next, seeing everything laid out in one place makes something clear to me:=
=20
there are really 2 distinct features at play here (note: when I say=20
"member" here, I mean non-static member):
1) An empty class which takes up no space when it is a member of another=20
class.
2) A Java-style inner class which is directly connected to its owner's=20
instance and can therefore access that object's members (as well as its=20
own).
When you combine these two in one type, you get what you call "inline=20
classes", when nested in a class). So "inline classes" in your definition=
=20
that aren't nested would just be empty classes.
By separating these concepts, you allow users to pick the functionality=20
they want to use. If they find it useful for a Java-style nested class to=
=20
have members or virtuals or whatever, why should you want to explicitly=20
prevent that? Why build that limitation into the system? Similarly, if they=
=20
find it useful to have a class that doesn't take up room that *isn't* a=20
Java-style inner class, again, why should you want to stop them?
The only time these two features interact has to do with the ability to=20
have zero-overhead combinations of the two: empty inner classes that take=
=20
up no space. Because Java-style inner class implementations would naturally=
=20
just stick a `this` pointer in the object, but if you want the object to=20
not take up space, you have to do things differently, with=20
pointer/reference trickery.
But in general, the problems incurred by #1.
What I find that I don't like about the proposal currently is that `inline=
=20
class` means two different things in different contexts. Outside of a class=
=20
definition, it means "empty type". Inside of one, it means "empty type that=
=20
can access the surrounding object's innards".
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
------=_Part_9_1625376422.1441134999175
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<br><br>On Tuesday, September 1, 2015 at 2:02:49 PM UTC-4, Matthew Woehlke =
wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8=
ex;border-left: 1px #ccc solid;padding-left: 1ex;">On 2015-08-30 15:13, Kla=
im - Jo=C3=ABl Lamotte wrote:
<br>> My suggestion was to discuss this outside the properties thread be=
cause
<br>> it's impossible to follow with the several sub-discussions ong=
oing.
<br>
<br>Also per Nicol's request, I'm attaching=C2=B9 my current draft.=
This isn't
<br>exactly formal proposal yet (in particular, there is no standardese at
<br>all), but I'm trying to at least start in the direction of a more f=
ormal
<br>paper rather than just paragraphs in an e-mail.
<br>
<br>The "zero storage object" rationale is... "lacking"=
, since I'm less
<br>familiar with that one. I'd appreciate help filling that out.
<br>
<br>Comments encouraged, of course. Also, please remind me what bits I
<br>forgot about, as I'm sure there are some.
<br>
<br>(=C2=B9 I hope to get this on github eventually, but I don't have t=
he ability
<br>to access my account at the moment.)
<br></blockquote><div><br>First, thank you for writing a concrete proposal.=
And more importantly, thank you for formatting it in plain text. Lots of p=
eople are too in live with throwing PDFs around these days...<br><br>Next, =
seeing everything laid out in one place makes something clear to me: there =
are really 2 distinct features at play here (note: when I say "member&=
quot; here, I mean non-static member):<br><br>1) An empty class which takes=
up no space when it is a member of another class.<br><br>2) A Java-style i=
nner class which is directly connected to its owner's instance and can =
therefore access that object's members (as well as its own).<br><br>Whe=
n you combine these two in one type, you get what you call "inline cla=
sses", when nested in a class). So "inline classes" in your =
definition that aren't nested would just be empty classes.<br><br>By se=
parating these concepts, you allow users to pick the functionality they wan=
t to use. If they find it useful for a Java-style nested class to have memb=
ers or virtuals or whatever, why should you want to explicitly prevent that=
? Why build that limitation into the system? Similarly, if they find it use=
ful to have a class that doesn't take up room that <i>isn't</i> a J=
ava-style inner class, again, why should you want to stop them?<br><br>The =
only time these two features interact has to do with the ability to have ze=
ro-overhead combinations of the two: empty inner classes that take up no sp=
ace. Because Java-style inner class implementations would naturally just st=
ick a `this` pointer in the object, but if you want the object to not take =
up space, you have to do things differently, with pointer/reference tricker=
y.<br><br>But in general, the problems incurred by #1.<br><br>What I find t=
hat I don't like about the proposal currently is that `inline class` me=
ans two different things in different contexts. Outside of a class definiti=
on, it means "empty type". Inside of one, it means "empty ty=
pe that can access the surrounding object's innards".<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_9_1625376422.1441134999175--
------=_Part_8_1078575196.1441134999175--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Tue, 01 Sep 2015 15:18:00 -0400
Raw View
On 2015-09-01 14:40, Miro Knejp wrote:
> I often see properties where only the setter is user-defined and the=20
> getter is left unchanged. It has its uses. But these languages (C#,=20
> Obj-C) also have some syntax/convention to assign to the actual=20
> backing variable (which the auto-generated getter is reading) from=20
> within the setter. If we don=E2=80=99t get that the usefulness drops
> significantly.
I'm not convinced that's an issue. If the backing data is inside the
inline class, it becomes difficult for the containing class to access it
directly without encapsulation or access protection problems. If it
isn't, it's not *that* much more typing to write 'auto get() const {
return m_value; }'. Especially if you have a PP macro to do the lifting
for you.
--=20
Matthew
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Tue, 01 Sep 2015 15:28:46 -0400
Raw View
On 2015-09-01 14:56, Nicol Bolas wrote:
> On Tuesday, September 1, 2015 at 2:28:52 PM UTC-4, Matthew Woehlke wrote:
>> Do you mean like this?
>>
>> class Value
>> {
>> public:
>> template <typename T> inline class Conversion
>> {
>> // ...generic implementation...
>> }
>>
>> template <typename T> Conversion<T> convert;
>> };
>>
>> template <typename T>
>> inline class value::conversion<T, enable_if<is_integral<T>>::type>
>> {
>> T operator()() const { /* access members of Value here */ }
>> };
>>
>> Value v;
>> long x = v.convert<long>();
>
> Yes, that's something like what I was looking for. And it's... weird.
Yeeeaah... *this* I'd probably call a kludge :-). It's more an example
that we ought to just allow partial specialization of member functions.
> Specifically, the definition of `Value::convert`. I'm not sure I understand
> what that's doing.
>
> Value::convert is a non-static data member. And those can't be variable
> templates, as far as I know.
Um... yeah, I think you're right. I haven't yet played with a compiler
that supports template variables in any form, so I'm fumbling a bit for
how they work. (Although, the reasons they wouldn't work relate to
storage, and so wouldn't apply to inline classes. We'd have to make a
specific, additional change to allow this use case however.)
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Miro Knejp <miro.knejp@gmail.com>
Date: Tue, 1 Sep 2015 22:17:10 +0200
Raw View
Am 01.09.2015 um 21:12 schrieb Matthew Woehlke:
> On 2015-09-01 14:06, Miro Knejp wrote:
>> On 01 Sep 2015, at 19:45 , Matthew Woehlke wrote:
>>> I disagree that this should magically become well-formed when you have =
a
>>> member of type P inside some class that has a 'baz'. What I was trying
>>> to say is that I... am not comfortable with your suggestion that the
>>> above is magically a CRTP template. If you want CRTP-like behavior...
>>> use CRTP.
>> If you have this->foo() in a class with a dependent base class it
>> also magically becomes well-formed when instantiated.
> Not really. The difference here is that you *have* a base class. And a
> template. Your example had neither.
>
>> If we assume name lookup in inline classes works as-if they were
>> derived from the parent, this is one consequence of that.
> In the example you gave, your class doesn't *have* a parent.
It does *conceptually* have one that is yet to be specified.
> The "as if"
> is for the class *definitions*, not the instantiations. (It can't be for
> the instantiations, because that would imply that the compiler generates
> new code for each instantiation. Which, okay, I get that you're asking
> for that anyway, but that's not what I intended.)
>
> I intended that code generation for inline classes is the same as for
> traditional classes; that is, only one instance of the code for a
> non-template inline class.
If the freestanding inline class doesn't contain anything that makes it=20
dependent on the parent's type it doens't need separate instantiations.=20
For me that's as a QoI issue.
>
>> Even if we omit the two-phase lookup thing, the compiler
>> still has to do *some* CRTP magic for non-empty inline classes.
> Can you elaborate?
inline classes with NSDMs need to know their offset in the parent. The=20
compiler needs to insert that offset magic either via instantiation or a=20
data field. In the former case it is equivalent to a compiler-generated=20
CRTP base class to retroactively modify the type/offset of "this". And=20
yes, I know you're still in the "only empty inline classes" world.
>
>> Even if only empty types are considered to begin with we can at least
>> anticipate non-empty inline classes in the future and not
>> artificially restrict our future selves.
> I still disagree with making things that don't look like templates
> suddenly behave like templates any more than necessary. It's one thing
> for multiple instances of the same type to become implicitly templates
> because of the offset magic needed to make their data members distinct.
> It's quite another thing for a freestanding class *that isn't a
> template* (at least, doesn't *look* like one) to turn into a template
> (i.e. not have its code compiled until it is actually instantiated).
So what? The Concepts TS introduces functions that don't *look* like=20
templates and yet are templates. Most of the time I don't need to care=20
whether I'm calling a template function or instantiating a template=20
class or not. That's the beauty of it.
>
>>> On 2015-09-01 12:59, Miro Knejp wrote:
>>>> On 01 Sep 2015, at 16:56 , Matthew Woehlke wrote:
>>>>> Repeating myself a bit here, but I see two possibilities: either the
>>>>> callee wants a mutable reference, and so the only way to make the cal=
l
>>>>> is to pass a reference to the property anyway, or else it takes a con=
st
>>>>> reference and passing the property reference could anyway only change
>>>>> the program semantics if the callee uses the value multiple times, an=
d
>>>>> the value changes in the mean time.
>>>> You have the same problems with regular references already. The value
>>>> of a reference you hold could change between accesses.
>>> True, but in the non-property case, a correctly designed API won't ever
>>> give you a reference.
>> A correctly designed API won=E2=80=99t do many things. If it=E2=80=99s n=
ot ill-formed
>> it needs to be specified.
> I don't understand where there is a problem here. If some method takes
> an int&, and I pass a property with a conversion to int&, such code can
> be compiled. What happens if that int changes is no more or less
> specified than if I passed an int& directly. Addressing that is
> completely out of scope.
>
> If that's not what you're talking about, please restate the problem,
> because I am confused...
>
It's only related to properties and I shouldn't have brought it up in=20
the first place.
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Tue, 1 Sep 2015 14:35:38 -0700 (PDT)
Raw View
------=_Part_5259_136602242.1441143338587
Content-Type: multipart/alternative;
boundary="----=_Part_5260_976811149.1441143338587"
------=_Part_5260_976811149.1441143338587
Content-Type: text/plain; charset=UTF-8
On Tuesday, September 1, 2015 at 4:15:45 PM UTC-4, Miro Knejp wrote:
>
> Am 01.09.2015 um 21:12 schrieb Matthew Woehlke:
> > On 2015-09-01 14:06, Miro Knejp wrote:
> >> Even if only empty types are considered to begin with we can at least
> >> anticipate non-empty inline classes in the future and not
> >> artificially restrict our future selves.
> > I still disagree with making things that don't look like templates
> > suddenly behave like templates any more than necessary. It's one thing
> > for multiple instances of the same type to become implicitly templates
> > because of the offset magic needed to make their data members distinct.
> > It's quite another thing for a freestanding class *that isn't a
> > template* (at least, doesn't *look* like one) to turn into a template
> > (i.e. not have its code compiled until it is actually instantiated).
> So what? The Concepts TS introduces functions that don't *look* like
> templates and yet are templates. Most of the time I don't need to care
> whether I'm calling a template function or instantiating a template
> class or not. That's the beauty of it.
>
Concepts gets away with it because the decision as to whether it's a
template function or not is made in the declaration. If none of the
parameter types are a concept, then it's not a template function.
In your suggestion, you can't even forward-declare an implicit template
inline class. The decision of whether it's a template or not is based on
the definition. So in the previous example, the compiler has to go
I'm not sure if we want compilers to have to do that. Let alone have users
do it.
I do not like the idea of using inline classes as a means to solve every
little bugbear of the language. If you don't like explicitly having to use
the CRTP, then a fix for that should be a *separate* proposal.
Yet another reason for the proposal to be divided into parts: each
individual part makes it clear what is being solved, and thus you can
discard suggestions that are clearly out-of-bounds. Like solving CRTP.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_5260_976811149.1441143338587
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Tuesday, September 1, 2015 at 4:15:45 PM UTC-4, Miro Knejp wrote:<blockq=
uote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-lef=
t: 1px #ccc solid;padding-left: 1ex;">Am 01.09.2015 um 21:12 schrieb Matthe=
w Woehlke:
<br>> On 2015-09-01 14:06, Miro Knejp wrote:
<br>>> Even if only empty types are considered to begin with we can a=
t least
<br>>> anticipate non-empty inline classes in the future and not
<br>>> artificially restrict our future selves.
<br>> I still disagree with making things that don't look like templ=
ates
<br>> suddenly behave like templates any more than necessary. It's o=
ne thing
<br>> for multiple instances of the same type to become implicitly templ=
ates
<br>> because of the offset magic needed to make their data members dist=
inct.
<br>> It's quite another thing for a freestanding class *that isn=
9;t a
<br>> template* (at least, doesn't *look* like one) to turn into a t=
emplate
<br>> (i.e. not have its code compiled until it is actually instantiated=
).
<br>So what? The Concepts TS introduces functions that don't *look* lik=
e=20
<br>templates and yet are templates. Most of the time I don't need to c=
are=20
<br>whether I'm calling a template function or instantiating a template=
=20
<br>class or not. That's the beauty of it.
<br></blockquote><div><br>Concepts gets away with it because the decision a=
s to whether it's a template function or not is made in the declaration=
.. If none of the parameter types are a concept, then it's not a templat=
e function.<br><br>In your suggestion, you can't even forward-declare a=
n implicit template inline class. The decision of whether it's a templa=
te or not is based on the definition. So in the previous example, the compi=
ler has to go<br><br>I'm not sure if we want compilers to have to do th=
at. Let alone have users do it.<br><br>I do not like the idea of using inli=
ne classes as a means to solve every little bugbear of the language. If you=
don't like explicitly having to use the CRTP, then a fix for that shou=
ld be a <i>separate</i> proposal.<br><br>Yet another reason for the proposa=
l to be divided into parts: each individual part makes it clear what is bei=
ng solved, and thus you can discard suggestions that are clearly out-of-bou=
nds. Like solving CRTP.</div><br>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_5260_976811149.1441143338587--
------=_Part_5259_136602242.1441143338587--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Tue, 1 Sep 2015 14:37:46 -0700 (PDT)
Raw View
------=_Part_3394_45558855.1441143466802
Content-Type: multipart/alternative;
boundary="----=_Part_3395_359470733.1441143466802"
------=_Part_3395_359470733.1441143466802
Content-Type: text/plain; charset=UTF-8
On Tuesday, September 1, 2015 at 5:35:39 PM UTC-4, Nicol Bolas wrote:
>
> In your suggestion, you can't even forward-declare an implicit template
> inline class. The decision of whether it's a template or not is based on
> the definition. So in the previous example, the compiler has to go
>
Oops, forgot to finish that sentence:
has to go through 2 whole member function declarations before it finds one
> that depends on the parent, and thus makes it a template.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_3395_359470733.1441143466802
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><br><br>On Tuesday, September 1, 2015 at 5:35:39 PM UTC-4,=
Nicol Bolas wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;mar=
gin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">In your sug=
gestion, you can't even forward-declare an implicit template inline cla=
ss. The decision of whether it's a template or not is based on the defi=
nition. So in the previous example, the compiler has to go<br></blockquote>=
<div><br>Oops, forgot to finish that sentence:<br><br><blockquote style=3D"=
margin: 0px 0px 0px 0.8ex; border-left: 1px solid rgb(204, 204, 204); paddi=
ng-left: 1ex;" class=3D"gmail_quote">has to go through 2 whole member funct=
ion declarations before it finds one that depends on the parent, and thus m=
akes it a template.</blockquote></div><br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_3395_359470733.1441143466802--
------=_Part_3394_45558855.1441143466802--
.
Author: Miro Knejp <miro.knejp@gmail.com>
Date: Wed, 2 Sep 2015 00:06:16 +0200
Raw View
This is a multi-part message in MIME format.
--------------020201010205020906080907
Content-Type: text/plain; charset=UTF-8; format=flowed
Am 01.09.2015 um 23:35 schrieb Nicol Bolas:
> On Tuesday, September 1, 2015 at 4:15:45 PM UTC-4, Miro Knejp wrote:
>
> Am 01.09.2015 um 21:12 schrieb Matthew Woehlke:
> > On 2015-09-01 14:06, Miro Knejp wrote:
> >> Even if only empty types are considered to begin with we can at
> least
> >> anticipate non-empty inline classes in the future and not
> >> artificially restrict our future selves.
> > I still disagree with making things that don't look like templates
> > suddenly behave like templates any more than necessary. It's one
> thing
> > for multiple instances of the same type to become implicitly
> templates
> > because of the offset magic needed to make their data members
> distinct.
> > It's quite another thing for a freestanding class *that isn't a
> > template* (at least, doesn't *look* like one) to turn into a
> template
> > (i.e. not have its code compiled until it is actually
> instantiated).
> So what? The Concepts TS introduces functions that don't *look* like
> templates and yet are templates. Most of the time I don't need to
> care
> whether I'm calling a template function or instantiating a template
> class or not. That's the beauty of it.
>
>
> Concepts gets away with it because the decision as to whether it's a
> template function or not is made in the declaration. If none of the
> parameter types are a concept, then it's not a template function.
>
> In your suggestion, you can't even forward-declare an implicit
> template inline class. The decision of whether it's a template or not
> is based on the definition. So in the previous example, the compiler
> has to go
Of course I can.
inline class P;
There, forward declared an inline class. The definiton doesn't affect
whether it's an implicit template or not. My approach implies that every
inline class is incomplete and an implicit template because the true
type of "this" is unknown, including empty inline classes without any
offset magic. Whether the compiler decides to only instantiate a single
specialization under the as-if rule or not I see as a QoI issue.
>
> I'm not sure if we want compilers to have to do that. Let alone have
> users do it.
>
> I do not like the idea of using inline classes as a means to solve
> every little bugbear of the language. If you don't like explicitly
> having to use the CRTP, then a fix for that should be a /separate/
> proposal.
Why is everyone so hung up on the CRTP comparison? It is not meant as a
tool to implement CRTP (that was just a side remark), it is a tool that
*may* be used by the compiler to implent it. I used the term because
people already understand how CRTP works.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--------------020201010205020906080907
Content-Type: text/html; charset=UTF-8
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
<body bgcolor="#FFFFFF" text="#000000">
Am 01.09.2015 um 23:35 schrieb Nicol Bolas:<br>
<blockquote
cite="mid:74682b80-94e7-4578-9933-e48f68c3045e@isocpp.org"
type="cite">On Tuesday, September 1, 2015 at 4:15:45 PM UTC-4,
Miro Knejp wrote:
<blockquote class="gmail_quote" style="margin: 0;margin-left:
0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">Am
01.09.2015 um 21:12 schrieb Matthew Woehlke:
<br>
> On 2015-09-01 14:06, Miro Knejp wrote:
<br>
>> Even if only empty types are considered to begin with
we can at least
<br>
>> anticipate non-empty inline classes in the future and
not
<br>
>> artificially restrict our future selves.
<br>
> I still disagree with making things that don't look like
templates
<br>
> suddenly behave like templates any more than necessary.
It's one thing
<br>
> for multiple instances of the same type to become
implicitly templates
<br>
> because of the offset magic needed to make their data
members distinct.
<br>
> It's quite another thing for a freestanding class *that
isn't a
<br>
> template* (at least, doesn't *look* like one) to turn into
a template
<br>
> (i.e. not have its code compiled until it is actually
instantiated).
<br>
So what? The Concepts TS introduces functions that don't *look*
like <br>
templates and yet are templates. Most of the time I don't need
to care <br>
whether I'm calling a template function or instantiating a
template <br>
class or not. That's the beauty of it.
<br>
</blockquote>
<div><br>
Concepts gets away with it because the decision as to whether
it's a template function or not is made in the declaration. If
none of the parameter types are a concept, then it's not a
template function.<br>
<br>
In your suggestion, you can't even forward-declare an implicit
template inline class. The decision of whether it's a template
or not is based on the definition. So in the previous example,
the compiler has to go<br>
</div>
</blockquote>
Of course I can.<br>
<br>
inline class P;<br>
<br>
There, forward declared an inline class. The definiton doesn't
affect whether it's an implicit template or not. My approach implies
that every inline class is incomplete and an implicit template
because the true type of "this" is unknown, including empty inline
classes without any offset magic. Whether the compiler decides to
only instantiate a single specialization under the as-if rule or not
I see as a QoI issue.<br>
<blockquote
cite="mid:74682b80-94e7-4578-9933-e48f68c3045e@isocpp.org"
type="cite">
<div><br>
I'm not sure if we want compilers to have to do that. Let alone
have users do it.<br>
<br>
I do not like the idea of using inline classes as a means to
solve every little bugbear of the language. If you don't like
explicitly having to use the CRTP, then a fix for that should be
a <i>separate</i> proposal.<br>
</div>
</blockquote>
Why is everyone so hung up on the CRTP comparison? It is not meant
as a tool to implement CRTP (that was just a side remark), it is a
tool that *may* be used by the compiler to implent it. I used the
term because people already understand how CRTP works.<br>
<br>
</body>
</html>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an email to <a href="mailto:std-proposals+unsubscribe@isocpp.org">std-proposals+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href="mailto:std-proposals@isocpp.org">std-proposals@isocpp.org</a>.<br />
Visit this group at <a href="http://groups.google.com/a/isocpp.org/group/std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/</a>.<br />
--------------020201010205020906080907--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Tue, 1 Sep 2015 15:44:07 -0700 (PDT)
Raw View
------=_Part_5228_19202718.1441147447237
Content-Type: multipart/alternative;
boundary="----=_Part_5229_844037573.1441147447237"
------=_Part_5229_844037573.1441147447237
Content-Type: text/plain; charset=UTF-8
On Tuesday, September 1, 2015 at 6:04:51 PM UTC-4, Miro Knejp wrote:
>
> Am 01.09.2015 um 23:35 schrieb Nicol Bolas:
>
> On Tuesday, September 1, 2015 at 4:15:45 PM UTC-4, Miro Knejp wrote:
>>
>> Am 01.09.2015 um 21:12 schrieb Matthew Woehlke:
>> > On 2015-09-01 14:06, Miro Knejp wrote:
>> >> Even if only empty types are considered to begin with we can at least
>> >> anticipate non-empty inline classes in the future and not
>> >> artificially restrict our future selves.
>> > I still disagree with making things that don't look like templates
>> > suddenly behave like templates any more than necessary. It's one thing
>> > for multiple instances of the same type to become implicitly templates
>> > because of the offset magic needed to make their data members distinct.
>> > It's quite another thing for a freestanding class *that isn't a
>> > template* (at least, doesn't *look* like one) to turn into a template
>> > (i.e. not have its code compiled until it is actually instantiated).
>> So what? The Concepts TS introduces functions that don't *look* like
>> templates and yet are templates. Most of the time I don't need to care
>> whether I'm calling a template function or instantiating a template
>> class or not. That's the beauty of it.
>>
>
> Concepts gets away with it because the decision as to whether it's a
> template function or not is made in the declaration. If none of the
> parameter types are a concept, then it's not a template function.
>
> In your suggestion, you can't even forward-declare an implicit template
> inline class. The decision of whether it's a template or not is based on
> the definition. So in the previous example, the compiler has to go
>
> Of course I can.
>
> inline class P;
>
> There, forward declared an inline class. The definiton doesn't affect
> whether it's an implicit template or not. My approach implies that every
> inline class is incomplete and an implicit template because the true type
> of "this" is unknown, including empty inline classes without any offset
> magic. Whether the compiler decides to only instantiate a single
> specialization under the as-if rule or not I see as a QoI issue.
>
You can consider it whatever you like, but the language doesn't allow such
ambiguity. Inline class definitions either are templates or they are not.
And therefore, either every separate instantiation of that template with a
different type is a separate class, or it is not.
>
> I'm not sure if we want compilers to have to do that. Let alone have users
> do it.
>
> I do not like the idea of using inline classes as a means to solve every
> little bugbear of the language. If you don't like explicitly having to use
> the CRTP, then a fix for that should be a *separate* proposal.
>
> Why is everyone so hung up on the CRTP comparison?
>
Because your concept *relies* upon an implicit use of CRTP to make your
code functional. Every use of an inline class requires access to the
containing class. And your idea requires template instantiation, that
declaring an inline class member implicitly passes the class type to that
inline class, performing template instantitation.
The CRTP is usually used for base classes, but it can also be used for
member types. Indeed, without the implicit CRTP usage you suggest, users
would be required to explicit use the CRTP when using derived inline
classes, if the base class is to be able to access members.
Therefore, your idea is an implicit use of CRTP. The comparison is
perfectly reasonable.
>
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_5229_844037573.1441147447237
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Tuesday, September 1, 2015 at 6:04:51 PM UTC-4, Miro Knejp wrote:<blockq=
uote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-lef=
t: 1px #ccc solid;padding-left: 1ex;">
=20
=20
=20
<div bgcolor=3D"#FFFFFF" text=3D"#000000">
Am 01.09.2015 um 23:35 schrieb Nicol Bolas:<br>
<blockquote type=3D"cite">On Tuesday, September 1, 2015 at 4:15:45 PM U=
TC-4,
Miro Knejp wrote:
<blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex=
;border-left:1px #ccc solid;padding-left:1ex">Am
01.09.2015 um 21:12 schrieb Matthew Woehlke:
<br>
> On 2015-09-01 14:06, Miro Knejp wrote:
<br>
>> Even if only empty types are considered to begin with
we can at least
<br>
>> anticipate non-empty inline classes in the future and
not
<br>
>> artificially restrict our future selves.
<br>
> I still disagree with making things that don't look like
templates
<br>
> suddenly behave like templates any more than necessary.
It's one thing
<br>
> for multiple instances of the same type to become
implicitly templates
<br>
> because of the offset magic needed to make their data
members distinct.
<br>
> It's quite another thing for a freestanding class *that
isn't a
<br>
> template* (at least, doesn't *look* like one) to turn into
a template
<br>
> (i.e. not have its code compiled until it is actually
instantiated).
<br>
So what? The Concepts TS introduces functions that don't *look*
like <br>
templates and yet are templates. Most of the time I don't need
to care <br>
whether I'm calling a template function or instantiating a
template <br>
class or not. That's the beauty of it.
<br>
</blockquote>
<div><br>
Concepts gets away with it because the decision as to whether
it's a template function or not is made in the declaration. If
none of the parameter types are a concept, then it's not a
template function.<br>
<br>
In your suggestion, you can't even forward-declare an implicit
template inline class. The decision of whether it's a template
or not is based on the definition. So in the previous example,
the compiler has to go<br>
</div>
</blockquote>
Of course I can.<br>
<br>
inline class P;<br>
<br>
There, forward declared an inline class. The definiton doesn't
affect whether it's an implicit template or not. My approach implie=
s
that every inline class is incomplete and an implicit template
because the true type of "this" is unknown, including empty i=
nline
classes without any offset magic. Whether the compiler decides to
only instantiate a single specialization under the as-if rule or not
I see as a QoI issue.<br></div></blockquote><div><br>You can consider i=
t whatever you like, but the language doesn't allow such ambiguity. Inl=
ine class definitions either are templates or they are not. And therefore, =
either every separate instantiation of that template with a different type =
is a separate class, or it is not.<i></i><br></div><blockquote class=3D"gma=
il_quote" style=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">
<div><br>
I'm not sure if we want compilers to have to do that. Let alone
have users do it.<br>
<br>
I do not like the idea of using inline classes as a means to
solve every little bugbear of the language. If you don't like
explicitly having to use the CRTP, then a fix for that should be
a <i>separate</i> proposal.<br>
</div>
</blockquote>
Why is everyone so hung up on the CRTP comparison?</div></blockquote><d=
iv><br>Because your concept <i>relies</i> upon an implicit use of CRTP to m=
ake your code functional. Every use of an inline class requires access to t=
he containing class. And your idea requires template instantiation, that de=
claring an inline class member implicitly passes the class type to that inl=
ine class, performing template instantitation.<br><br>The CRTP is usually u=
sed for base classes, but it can also be used for member types. Indeed, wit=
hout the implicit CRTP usage you suggest, users would be required to explic=
it use the CRTP when using derived inline classes, if the base class is to =
be able to access members.<br><br>Therefore, your idea is an implicit use o=
f CRTP. The comparison is perfectly reasonable.<br></div><blockquote class=
=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #cc=
c solid;padding-left: 1ex;">
</blockquote>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_5229_844037573.1441147447237--
------=_Part_5228_19202718.1441147447237--
.
Author: Miro Knejp <miro.knejp@gmail.com>
Date: Wed, 2 Sep 2015 01:18:15 +0200
Raw View
This is a multi-part message in MIME format.
--------------060105010007010106020500
Content-Type: text/plain; charset=UTF-8; format=flowed
Am 02.09.2015 um 00:44 schrieb Nicol Bolas:
> On Tuesday, September 1, 2015 at 6:04:51 PM UTC-4, Miro Knejp wrote:
>
> Am 01.09.2015 um 23:35 schrieb Nicol Bolas:
>> On Tuesday, September 1, 2015 at 4:15:45 PM UTC-4, Miro Knejp wrote:
>>
>> Am 01.09.2015 um 21:12 schrieb Matthew Woehlke:
>> > On 2015-09-01 14:06, Miro Knejp wrote:
>> >> Even if only empty types are considered to begin with we
>> can at least
>> >> anticipate non-empty inline classes in the future and not
>> >> artificially restrict our future selves.
>> > I still disagree with making things that don't look like
>> templates
>> > suddenly behave like templates any more than necessary.
>> It's one thing
>> > for multiple instances of the same type to become
>> implicitly templates
>> > because of the offset magic needed to make their data
>> members distinct.
>> > It's quite another thing for a freestanding class *that
>> isn't a
>> > template* (at least, doesn't *look* like one) to turn into
>> a template
>> > (i.e. not have its code compiled until it is actually
>> instantiated).
>> So what? The Concepts TS introduces functions that don't
>> *look* like
>> templates and yet are templates. Most of the time I don't
>> need to care
>> whether I'm calling a template function or instantiating a
>> template
>> class or not. That's the beauty of it.
>>
>>
>> Concepts gets away with it because the decision as to whether
>> it's a template function or not is made in the declaration. If
>> none of the parameter types are a concept, then it's not a
>> template function.
>>
>> In your suggestion, you can't even forward-declare an implicit
>> template inline class. The decision of whether it's a template or
>> not is based on the definition. So in the previous example, the
>> compiler has to go
> Of course I can.
>
> inline class P;
>
> There, forward declared an inline class. The definiton doesn't
> affect whether it's an implicit template or not. My approach
> implies that every inline class is incomplete and an implicit
> template because the true type of "this" is unknown, including
> empty inline classes without any offset magic. Whether the
> compiler decides to only instantiate a single specialization under
> the as-if rule or not I see as a QoI issue.
>
>
> You can consider it whatever you like, but the language doesn't allow
> such ambiguity. Inline class definitions either are templates or they
> are not. And therefore, either every separate instantiation of that
> template with a different type is a separate class, or it is not.
There is no ambiguity. As I clearly described *every freestanding inline
class is an implicit template*. Period. The compiler can do under the
as-if rule whatever the compiler damn pleases.
>
>>
>> I'm not sure if we want compilers to have to do that. Let alone
>> have users do it.
>>
>> I do not like the idea of using inline classes as a means to
>> solve every little bugbear of the language. If you don't like
>> explicitly having to use the CRTP, then a fix for that should be
>> a /separate/ proposal.
> Why is everyone so hung up on the CRTP comparison?
>
>
> Because your concept /relies/ upon an implicit use of CRTP to make
> your code functional.
Yes, it *relies* on the pattern as an implementation detail, it is *not*
a language tool for CRTP. In many implementations virtual functions rely
on function pointer tables. That doesn't make virtual functions a
language tool for function pointer tables. The feature wording doesn't
have to mention templates or CRTP at all *if* it isn't required to
describe the observable behavior.
> Every use of an inline class requires access to the containing class.
> And your idea requires template instantiation, that declaring an
> inline class member implicitly passes the class type to that inline
> class, performing template instantitation.
>
> The CRTP is usually used for base classes, but it can also be used for
> member types. Indeed, without the implicit CRTP usage you suggest,
> users would be required to explicit use the CRTP when using derived
> inline classes, if the base class is to be able to access members.
>
> Therefore, your idea is an implicit use of CRTP. The comparison is
> perfectly reasonable.
I don't mind the comparison. I am puzzled by why people demand the CRTP
aspect to somehow become its own proposal when I use CRTP as an
implementation detail, not an advertized feature.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--------------060105010007010106020500
Content-Type: text/html; charset=UTF-8
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
<body bgcolor="#FFFFFF" text="#000000">
Am 02.09.2015 um 00:44 schrieb Nicol Bolas:<br>
<blockquote
cite="mid:cc7764b7-b242-416f-8796-4da17dac0567@isocpp.org"
type="cite">On Tuesday, September 1, 2015 at 6:04:51 PM UTC-4,
Miro Knejp wrote:
<blockquote class="gmail_quote" style="margin: 0;margin-left:
0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
<div bgcolor="#FFFFFF" text="#000000"> Am 01.09.2015 um 23:35
schrieb Nicol Bolas:<br>
<blockquote type="cite">On Tuesday, September 1, 2015 at
4:15:45 PM UTC-4, Miro Knejp wrote:
<blockquote class="gmail_quote"
style="margin:0;margin-left:0.8ex;border-left:1px #ccc
solid;padding-left:1ex">Am 01.09.2015 um 21:12 schrieb
Matthew Woehlke: <br>
> On 2015-09-01 14:06, Miro Knejp wrote: <br>
>> Even if only empty types are considered to begin
with we can at least <br>
>> anticipate non-empty inline classes in the future
and not <br>
>> artificially restrict our future selves. <br>
> I still disagree with making things that don't look
like templates <br>
> suddenly behave like templates any more than
necessary. It's one thing <br>
> for multiple instances of the same type to become
implicitly templates <br>
> because of the offset magic needed to make their data
members distinct. <br>
> It's quite another thing for a freestanding class
*that isn't a <br>
> template* (at least, doesn't *look* like one) to turn
into a template <br>
> (i.e. not have its code compiled until it is actually
instantiated). <br>
So what? The Concepts TS introduces functions that don't
*look* like <br>
templates and yet are templates. Most of the time I don't
need to care <br>
whether I'm calling a template function or instantiating a
template <br>
class or not. That's the beauty of it. <br>
</blockquote>
<div><br>
Concepts gets away with it because the decision as to
whether it's a template function or not is made in the
declaration. If none of the parameter types are a concept,
then it's not a template function.<br>
<br>
In your suggestion, you can't even forward-declare an
implicit template inline class. The decision of whether
it's a template or not is based on the definition. So in
the previous example, the compiler has to go<br>
</div>
</blockquote>
Of course I can.<br>
<br>
inline class P;<br>
<br>
There, forward declared an inline class. The definiton doesn't
affect whether it's an implicit template or not. My approach
implies that every inline class is incomplete and an implicit
template because the true type of "this" is unknown, including
empty inline classes without any offset magic. Whether the
compiler decides to only instantiate a single specialization
under the as-if rule or not I see as a QoI issue.<br>
</div>
</blockquote>
<div><br>
You can consider it whatever you like, but the language doesn't
allow such ambiguity. Inline class definitions either are
templates or they are not. And therefore, either every separate
instantiation of that template with a different type is a
separate class, or it is not.<br>
</div>
</blockquote>
There is no ambiguity. As I clearly described *every freestanding
inline class is an implicit template*. Period. The compiler can do
under the as-if rule whatever the compiler damn pleases.<br>
<blockquote
cite="mid:cc7764b7-b242-416f-8796-4da17dac0567@isocpp.org"
type="cite">
<blockquote class="gmail_quote" style="margin: 0;margin-left:
0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
<div bgcolor="#FFFFFF" text="#000000">
<blockquote type="cite">
<div><br>
I'm not sure if we want compilers to have to do that. Let
alone have users do it.<br>
<br>
I do not like the idea of using inline classes as a means
to solve every little bugbear of the language. If you
don't like explicitly having to use the CRTP, then a fix
for that should be a <i>separate</i> proposal.<br>
</div>
</blockquote>
Why is everyone so hung up on the CRTP comparison?</div>
</blockquote>
<div><br>
Because your concept <i>relies</i> upon an implicit use of CRTP
to make your code functional. </div>
</blockquote>
Yes, it *relies* on the pattern as an implementation detail, it is
*not* a language tool for CRTP. In many implementations virtual
functions rely on function pointer tables. That doesn't make virtual
functions a language tool for function pointer tables. The feature
wording doesn't have to mention templates or CRTP at all *if* it
isn't required to describe the observable behavior.<br>
<blockquote
cite="mid:cc7764b7-b242-416f-8796-4da17dac0567@isocpp.org"
type="cite">
<div>Every use of an inline class requires access to the
containing class. And your idea requires template instantiation,
that declaring an inline class member implicitly passes the
class type to that inline class, performing template
instantitation.<br>
<br>
The CRTP is usually used for base classes, but it can also be
used for member types. Indeed, without the implicit CRTP usage
you suggest, users would be required to explicit use the CRTP
when using derived inline classes, if the base class is to be
able to access members.<br>
<br>
Therefore, your idea is an implicit use of CRTP. The comparison
is perfectly reasonable.<br>
</div>
</blockquote>
I don't mind the comparison. I am puzzled by why people demand the
CRTP aspect to somehow become its own proposal when I use CRTP as an
implementation detail, not an advertized feature.<br>
<br>
</body>
</html>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an email to <a href="mailto:std-proposals+unsubscribe@isocpp.org">std-proposals+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href="mailto:std-proposals@isocpp.org">std-proposals@isocpp.org</a>.<br />
Visit this group at <a href="http://groups.google.com/a/isocpp.org/group/std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/</a>.<br />
--------------060105010007010106020500--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Tue, 1 Sep 2015 17:18:28 -0700 (PDT)
Raw View
------=_Part_267_899758547.1441153108473
Content-Type: multipart/alternative;
boundary="----=_Part_268_447190504.1441153108473"
------=_Part_268_447190504.1441153108473
Content-Type: text/plain; charset=UTF-8
On Tuesday, September 1, 2015 at 7:16:50 PM UTC-4, Miro Knejp wrote:
>
> Am 02.09.2015 um 00:44 schrieb Nicol Bolas:
>
> On Tuesday, September 1, 2015 at 6:04:51 PM UTC-4, Miro Knejp wrote:
>>
>> Am 01.09.2015 um 23:35 schrieb Nicol Bolas:
>>
> I'm not sure if we want compilers to have to do that. Let alone have users
>> do it.
>>
>> I do not like the idea of using inline classes as a means to solve every
>> little bugbear of the language. If you don't like explicitly having to use
>> the CRTP, then a fix for that should be a *separate* proposal.
>>
>> Why is everyone so hung up on the CRTP comparison?
>>
>
> Because your concept *relies* upon an implicit use of CRTP to make your
> code functional.
>
> Yes, it *relies* on the pattern as an implementation detail, it is *not* a
> language tool for CRTP. In many implementations virtual functions rely on
> function pointer tables. That doesn't make virtual functions a language
> tool for function pointer tables. The feature wording doesn't have to
> mention templates or CRTP at all *if* it isn't required to describe the
> observable behavior.
>
It is no more an "implementation detail" than C++ lambdas generating an
anonymous type that has `operator()` overloaded is an "implementation
detail". Those are not details that get to vary; they are observable
behavior and therefore must be specified by the standard.
Here's some observable behavior: if all inline classes are templates, then
this is not legal:
inline class X;
X *p = ...;
For any `...` you might imagine.
Why is this illegal? Because there *is no class X* (inline or otherwise);
there is a *template* X, which may be instantiated with template parameters
to produce a multitude of classes. That's observable behavior, and
therefore it must be standardized. Because if X were a real class of some
kind (like the original inline class proposal suggests), then this code
could potentially be legal.
To allow this to merely be an "implementation detail" which cannot be
observed requires that inline classes be some fundamentally new construct,
neither a class nor a template. At which point, you're going to have a hell
of a lot harder time getting it standardized.
It's easy to say, "well, it just does this". It's much harder to make that
actually work with the language. And if you truly want this to be an
entirely new classification of construct, then you need to put forth some
good-faith effort to work through the language problems it creates.
> Every use of an inline class requires access to the containing class. And
> your idea requires template instantiation, that declaring an inline class
> member implicitly passes the class type to that inline class, performing
> template instantitation.
>
> The CRTP is usually used for base classes, but it can also be used for
> member types. Indeed, without the implicit CRTP usage you suggest, users
> would be required to explicit use the CRTP when using derived inline
> classes, if the base class is to be able to access members.
>
> Therefore, your idea is an implicit use of CRTP. The comparison is
> perfectly reasonable.
>
> I don't mind the comparison. I am puzzled by why people demand the CRTP
> aspect to somehow become its own proposal when I use CRTP as an
> implementation detail, not an advertized feature.
>
Whether you want it to be an implementation detail or not, once that's
there, it becomes a *feature*. And people will use it as such; people will
use these classes to avoid using the CRTP.
Furthermore, people may want to use the CRTP part *without* the other
limitations of inline classes.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_268_447190504.1441153108473
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<br><br>On Tuesday, September 1, 2015 at 7:16:50 PM UTC-4, Miro Knejp wrote=
:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bo=
rder-left: 1px #ccc solid;padding-left: 1ex;">
=20
=20
=20
<div bgcolor=3D"#FFFFFF" text=3D"#000000">
Am 02.09.2015 um 00:44 schrieb Nicol Bolas:<br>
<blockquote type=3D"cite">On Tuesday, September 1, 2015 at 6:04:51 PM U=
TC-4,
Miro Knejp wrote:
<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"> Am 01.09.2015 um 23:35
schrieb Nicol Bolas:<br>
</div></blockquote></blockquote><blockquote type=3D"cite">
<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">
<blockquote type=3D"cite">
<div>
I'm not sure if we want compilers to have to do that. Let
alone have users do it.<br>
<br>
I do not like the idea of using inline classes as a means
to solve every little bugbear of the language. If you
don't like explicitly having to use the CRTP, then a fix
for that should be a <i>separate</i> proposal.<br>
</div>
</blockquote>
Why is everyone so hung up on the CRTP comparison?</div>
</blockquote>
<div><br>
Because your concept <i>relies</i> upon an implicit use of CRTP
to make your code functional. </div>
</blockquote>
Yes, it *relies* on the pattern as an implementation detail, it is
*not* a language tool for CRTP. In many implementations virtual
functions rely on function pointer tables. That doesn't make virtua=
l
functions a language tool for function pointer tables. The feature
wording doesn't have to mention templates or CRTP at all *if* it
isn't required to describe the observable behavior.</div></blockquo=
te><div><br>It is no more an "implementation detail" than C++ lam=
bdas generating an anonymous type that has `operator()` overloaded is an &q=
uot;implementation detail". Those are not details that get to vary; th=
ey are observable behavior and therefore must be specified by the standard.=
<br><br>Here's some observable behavior: if all inline classes are temp=
lates, then this is not legal:<br><br><div class=3D"prettyprint" style=3D"b=
ackground-color: rgb(250, 250, 250); border-color: rgb(187, 187, 187); bord=
er-style: solid; border-width: 1px; word-wrap: break-word;"><code class=3D"=
prettyprint"><div class=3D"subprettyprint"><span style=3D"color: #008;" cla=
ss=3D"styled-by-prettify">inline</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"> </span><span style=3D"color: #008;" class=3D"style=
d-by-prettify">class</span><span style=3D"color: #000;" class=3D"styled-by-=
prettify"> X</span><span style=3D"color: #660;" class=3D"styled-by-prettify=
">;</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br><br=
>X </span><span style=3D"color: #660;" class=3D"styled-by-prettify">*</span=
><span style=3D"color: #000;" class=3D"styled-by-prettify">p </span><span s=
tyle=3D"color: #660;" class=3D"styled-by-prettify">=3D</span><span style=3D=
"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #=
660;" class=3D"styled-by-prettify">...;</span><span style=3D"color: #000;" =
class=3D"styled-by-prettify"><br></span></div></code></div><br>For any `...=
` you might imagine.<br><br>Why is this illegal? Because there <i>is no cla=
ss X</i> (inline or otherwise); there is a <i>template</i> X, which may be =
instantiated with template parameters to produce a multitude of classes. Th=
at's observable behavior, and therefore it must be standardized. Becaus=
e if X were a real class of some kind (like the original inline class propo=
sal suggests), then this code could potentially be legal.<br><br>To allow t=
his to merely be an "implementation detail" which cannot be obser=
ved requires that inline classes be some fundamentally new construct, neith=
er a class nor a template. At which point, you're going to have a hell =
of a lot harder time getting it standardized.<br><br>It's easy to say, =
"well, it just does this". It's much harder to make that actu=
ally work with the language. And if you truly want this to be an entirely n=
ew classification of construct, then you need to put forth some good-faith =
effort to work through the language problems it creates.<br></div><blockquo=
te 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"#00000=
0">
<blockquote type=3D"cite">
<div>Every use of an inline class requires access to the
containing class. And your idea requires template instantiation,
that declaring an inline class member implicitly passes the
class type to that inline class, performing template
instantitation.<br>
<br>
The CRTP is usually used for base classes, but it can also be
used for member types. Indeed, without the implicit CRTP usage
you suggest, users would be required to explicit use the CRTP
when using derived inline classes, if the base class is to be
able to access members.<br>
<br>
Therefore, your idea is an implicit use of CRTP. The comparison
is perfectly reasonable.<br>
</div>
</blockquote>
I don't mind the comparison. I am puzzled by why people demand the
CRTP aspect to somehow become its own proposal when I use CRTP as an
implementation detail, not an advertized feature.<br></div></blockquote=
><div><br>Whether you want it to be an implementation detail or not, once t=
hat's there, it becomes a <i>feature</i>. And people will use it as suc=
h; people will use these classes to avoid using the CRTP.<br><br>Furthermor=
e, people may want to use the CRTP part <i>without</i> the other limitation=
s of inline classes.<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_268_447190504.1441153108473--
------=_Part_267_899758547.1441153108473--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Wed, 02 Sep 2015 09:45:06 -0400
Raw View
On 2015-09-01 19:18, Miro Knejp wrote:
> I don't mind the comparison. I am puzzled by why people demand the CRTP
> aspect to somehow become its own proposal when I use CRTP as an
> implementation detail, not an advertized feature.
Because it *is* an advertised feature, whether you call it CRTP or not.
Because making CRTP better is worthwhile in its own right and should not
be limited to inline classes. Because, as Nicol rightly points out,
making them work that way makes them significantly different from any
existing construct. (Though, I'll note, I also don't allow naming the
type of a freestanding inline class. However that was more for
consistency with sizeof being illegal for the same, and because I don't
see a use case.)
That second point is the big one; inline classes should not be a
substitute for CRTP. If one is overly tempted to use them as such - and
with your implementation, I think that would happen - then we have done
something wrong. Repeating Nicol again, the correct thing to do is make
CRTP *in general* easier, not provide an alternative with a different
set of limitations.
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Wed, 2 Sep 2015 07:33:41 -0700 (PDT)
Raw View
------=_Part_5988_2119229236.1441204421900
Content-Type: multipart/alternative;
boundary="----=_Part_5989_1523647716.1441204421901"
------=_Part_5989_1523647716.1441204421901
Content-Type: text/plain; charset=UTF-8
On Wednesday, September 2, 2015 at 9:45:25 AM UTC-4, Matthew Woehlke wrote:
> (Though, I'll note, I also don't allow naming the
>
type of a freestanding inline class. However that was more for
> consistency with sizeof being illegal for the same, and because I don't
> see a use case.)
>
I found that to be quite odd in the proposal, actually. You are required to
name the type of a freestanding inline class. You name it when you
declare/define it, and you name it when you declare an instance of that
type (as a member) or when you use it as a base class. And yet, you're
allowed to name a *nested* inline class just fine.
I'm not sure what the point of that distinction is. You shouldn't restrict
obvious syntax arbitrarily (particularly in surprising ways, since again,
you're required to name them in other circumstances), simply because you
don't know of a use for it.
Furthermore, there are uses for it. For example:
inline class Base {void Func() {...}};
class Foo
{
inline class Derived : public Base
{
void Func() {Base::Func();}
}
};
I see no reason to forbid this.
Then there's this:
template<typename T> inline class Base<T> {...};
template<typename T> void Process(Base<T> &ref);
The `Process` function takes any member derived from `Base`. The CRTP is
used to allow static polymorphism. But all this requires that it be
possible to name the base class.
Furthermore, such arbitrary restrictions only increase the standard burden
of having to pick and choose the places where you're allowed to name types
that have names. You should not restrict inline classes any more than is *strictly
necessary* to get the functionality you want.
And `sizeof` is no excuse. `sizeof` for an inline class should simply be
what you get for the size of *any* empty class. I see no reason to forbid
the user from asking that question, particularly since they could be in
template code or otherwise deduced areas (auto). After all, your goal is
not to say that the type itself doesn't take up space. Rather, the goal is
to do what the empty base optimization does: say that *variables* of that
type don't take up space when declared as members.
Then you say that you can only declare them as members ;) So you satisfy
the expected requirements of classes and objects, and you still get what
you want.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_5989_1523647716.1441204421901
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Wednesday, September 2, 2015 at 9:45:25 AM UTC-4, Matth=
ew Woehlke wrote:<br><blockquote style=3D"margin: 0px 0px 0px 0.8ex; border=
-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;" class=3D"gmail_quo=
te">(Though, I'll note, I also don't allow naming the
<br></blockquote><blockquote class=3D"gmail_quote" style=3D"margin: 0;margi=
n-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">type of a fre=
estanding inline class. However that was more for
<br>consistency with sizeof being illegal for the same, and because I don&#=
39;t
<br>see a use case.)
<br></blockquote><div><br>I found that to be quite odd in the proposal, act=
ually. You are required to name the type of a freestanding inline class. Yo=
u name it when you declare/define it, and you name it when you declare an i=
nstance of that type (as a member) or when you use it as a base class. And =
yet, you're allowed to name a <i>nested</i> inline class just fine.<br>=
<br>I'm not sure what the point of that distinction is. You shouldn'=
;t restrict obvious syntax arbitrarily (particularly in surprising ways, si=
nce again, you're required to name them in other circumstances), simply=
because you don't know of a use for it.<br><br>Furthermore, there are =
uses for it. For example:<br><br><div class=3D"prettyprint" style=3D"backgr=
ound-color: rgb(250, 250, 250); border-color: rgb(187, 187, 187); border-st=
yle: solid; border-width: 1px; word-wrap: break-word;"><code class=3D"prett=
yprint"><div class=3D"subprettyprint"><span style=3D"color: #008;" class=3D=
"styled-by-prettify">inline</span><span style=3D"color: #000;" class=3D"sty=
led-by-prettify"> </span><span style=3D"color: #008;" class=3D"styled-by-pr=
ettify">class</span><span style=3D"color: #000;" class=3D"styled-by-prettif=
y"> </span><span style=3D"color: #606;" class=3D"styled-by-prettify">Base</=
span><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><spa=
n style=3D"color: #660;" class=3D"styled-by-prettify">{</span><span style=
=3D"color: #008;" class=3D"styled-by-prettify">void</span><span style=3D"co=
lor: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #606=
;" class=3D"styled-by-prettify">Func</span><span style=3D"color: #660;" cla=
ss=3D"styled-by-prettify">()</span><span style=3D"color: #000;" class=3D"st=
yled-by-prettify"> </span><span style=3D"color: #660;" class=3D"styled-by-p=
rettify">{...}};</span><span style=3D"color: #000;" class=3D"styled-by-pret=
tify"><br><br></span><span style=3D"color: #008;" class=3D"styled-by-pretti=
fy">class</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> =
</span><span style=3D"color: #606;" class=3D"styled-by-prettify">Foo</span>=
<span style=3D"color: #000;" class=3D"styled-by-prettify"><br></span><span =
style=3D"color: #660;" class=3D"styled-by-prettify">{</span><span style=3D"=
color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 </span><span style=3D=
"color: #008;" class=3D"styled-by-prettify">inline</span><span style=3D"col=
or: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #008;=
" class=3D"styled-by-prettify">class</span><span style=3D"color: #000;" cla=
ss=3D"styled-by-prettify"> </span><span style=3D"color: #606;" class=3D"sty=
led-by-prettify">Derived</span><span style=3D"color: #000;" class=3D"styled=
-by-prettify"> </span><span style=3D"color: #660;" class=3D"styled-by-prett=
ify">:</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> </s=
pan><span style=3D"color: #008;" class=3D"styled-by-prettify">public</span>=
<span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span sty=
le=3D"color: #606;" class=3D"styled-by-prettify">Base</span><span style=3D"=
color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 </span><span style=3D=
"color: #660;" class=3D"styled-by-prettify">{</span><span style=3D"color: #=
000;" class=3D"styled-by-prettify"><br>=C2=A0 =C2=A0 </span><span style=3D"=
color: #008;" class=3D"styled-by-prettify">void</span><span style=3D"color:=
#000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #606;" c=
lass=3D"styled-by-prettify">Func</span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">()</span><span style=3D"color: #000;" class=3D"styl=
ed-by-prettify"> </span><span style=3D"color: #660;" class=3D"styled-by-pre=
ttify">{</span><span style=3D"color: #606;" class=3D"styled-by-prettify">Ba=
se</span><span style=3D"color: #660;" class=3D"styled-by-prettify">::</span=
><span style=3D"color: #606;" class=3D"styled-by-prettify">Func</span><span=
style=3D"color: #660;" class=3D"styled-by-prettify">();}</span><span style=
=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 </span><span styl=
e=3D"color: #660;" class=3D"styled-by-prettify">}</span><span style=3D"colo=
r: #000;" class=3D"styled-by-prettify"><br></span><span style=3D"color: #66=
0;" class=3D"styled-by-prettify">};</span></div></code></div><br>I see no r=
eason to forbid this.<br><br>Then there's this:<br><br><div class=3D"pr=
ettyprint" style=3D"background-color: rgb(250, 250, 250); border-color: rgb=
(187, 187, 187); border-style: solid; border-width: 1px; word-wrap: break-w=
ord;"><code class=3D"prettyprint"><div class=3D"subprettyprint"><span style=
=3D"color: #008;" class=3D"styled-by-prettify">template</span><span style=
=3D"color: #660;" class=3D"styled-by-prettify"><</span><span style=3D"co=
lor: #008;" class=3D"styled-by-prettify">typename</span><span style=3D"colo=
r: #000;" class=3D"styled-by-prettify"> T</span><span style=3D"color: #660;=
" class=3D"styled-by-prettify">></span><span style=3D"color: #000;" clas=
s=3D"styled-by-prettify"> </span><span style=3D"color: #008;" class=3D"styl=
ed-by-prettify">inline</span><span style=3D"color: #000;" class=3D"styled-b=
y-prettify"> </span><span style=3D"color: #008;" class=3D"styled-by-prettif=
y">class</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> <=
/span><span style=3D"color: #606;" class=3D"styled-by-prettify">Base</span>=
<span style=3D"color: #660;" class=3D"styled-by-prettify"><</span><span =
style=3D"color: #000;" class=3D"styled-by-prettify">T</span><span style=3D"=
color: #660;" class=3D"styled-by-prettify">></span><span style=3D"color:=
#000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #660;" c=
lass=3D"styled-by-prettify">{...};</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"><br><br></span><span style=3D"color: #008;" class=
=3D"styled-by-prettify">template</span><span style=3D"color: #660;" class=
=3D"styled-by-prettify"><</span><span style=3D"color: #008;" class=3D"st=
yled-by-prettify">typename</span><span style=3D"color: #000;" class=3D"styl=
ed-by-prettify"> T</span><span style=3D"color: #660;" class=3D"styled-by-pr=
ettify">></span><span style=3D"color: #000;" class=3D"styled-by-prettify=
"> </span><span style=3D"color: #008;" class=3D"styled-by-prettify">void</s=
pan><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span=
style=3D"color: #606;" class=3D"styled-by-prettify">Process</span><span st=
yle=3D"color: #660;" class=3D"styled-by-prettify">(</span><span style=3D"co=
lor: #606;" class=3D"styled-by-prettify">Base</span><span style=3D"color: #=
660;" class=3D"styled-by-prettify"><</span><span style=3D"color: #000;" =
class=3D"styled-by-prettify">T</span><span style=3D"color: #660;" class=3D"=
styled-by-prettify">></span><span style=3D"color: #000;" class=3D"styled=
-by-prettify"> </span><span style=3D"color: #660;" class=3D"styled-by-prett=
ify">&</span><span style=3D"color: #008;" class=3D"styled-by-prettify">=
ref</span><span style=3D"color: #660;" class=3D"styled-by-prettify">);</spa=
n></div></code></div><br>The `Process` function takes any member derived fr=
om `Base`. The CRTP is used to allow static polymorphism. But all this requ=
ires that it be possible to name the base class.<br><br>Furthermore, such a=
rbitrary restrictions only increase the standard burden of having to pick a=
nd choose the places where you're allowed to name types that have names=
.. You should not restrict inline classes any more than is <i>strictly neces=
sary</i> to get the functionality you want.<br><br>And `sizeof` is no excus=
e. `sizeof` for an inline class should simply be what you get for the size =
of <i>any</i> empty class. I see no reason to forbid the user from asking t=
hat question, particularly since they could be in template code or otherwis=
e deduced areas (auto). After all, your goal is not to say that the type it=
self doesn't take up space. Rather, the goal is to do what the empty ba=
se optimization does: say that <i>variables</i> of that type don't take=
up space when declared as members.<br><br>Then you say that you can only d=
eclare them as members ;) So you satisfy the expected requirements of class=
es and objects, and you still get what you want.</div></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_5989_1523647716.1441204421901--
------=_Part_5988_2119229236.1441204421900--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Wed, 02 Sep 2015 11:18:02 -0400
Raw View
On 2015-09-02 10:33, Nicol Bolas wrote:
> On Wednesday, September 2, 2015 at 9:45:25 AM UTC-4, Matthew Woehlke wrote:
>> (Though, I'll note, I also don't allow naming the type of a
>> freestanding inline class. However that was more for consistency
>> with sizeof being illegal for the same, and because I don't see a
>> use case.)
>
> I found that to be quite odd in the proposal, actually. You are required to
> name the type of a freestanding inline class. You name it when you
> declare/define it, and you name it when you declare an instance of that
> type (as a member) or when you use it as a base class. And yet, you're
> allowed to name a *nested* inline class just fine.
The "trouble" is that a freestanding inline class is incomplete (in a
storage sense). Recall in particular that you cannot take its size.
Because of that, it seemed convenient to not allow using it as a
parameter either.
On the other hand, it is an artificial restriction. If being able to use
the type name despite being illegal to take its size is not considered a
problem, I have no issue allowing that.
Also see below...
> inline class Base {void Func() {...}};
>
> class Foo
> {
> inline class Derived : public Base
> {
> void Func() {Base::Func();}
> }
> };
>
> I see no reason to forbid this.
Sorry, the wording in my write-up was overly broad / poor. It was always
intended that the above is permitted. It's (only) using the type name as
a variable or parameter that would not be allowed. But...
> Then there's this:
>
> template<typename T> inline class Base<T> {...};
>
> template<typename T> void Process(Base<T> &ref);
This is *slightly* different because of the template... but on further
consideration, I think you are correct; this needs to be permitted.
(Consider e.g. the std::property<Prop> case...)
I hereby remove that restriction.
> And `sizeof` is no excuse. `sizeof` for an inline class should simply be
> what you get for the size of *any* empty class.
Here's the problem:
inline class Foo { ... };
struct Dog { Foo foo; ... };
struct Cat { Foo foo; ... };
static_assert(sizeof(Dog::foo) == sizeof(Dog));
static_assert(sizeof(Cat::foo) == sizeof(Cat));
// what is sizeof(Foo)?
I guess what you are suggesting is that the size of a FIC should be the
size of 'class {}'? (Should we explicitly say that a CIC member written
as a FIC type is actually a different type? I.e. in the above,
decltype(Dog::foo) != decltype(Foo)? If implemented as implicit
subclassing, I think this would be okay, since the sizeof an instance
already can change depending on if the visible type is a subclass or
base class. in a sense we would be codifying EBO for this case.)
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Wed, 02 Sep 2015 12:06:29 -0400
Raw View
On 2015-09-01 15:16, Nicol Bolas wrote:
> seeing everything laid out in one place makes something clear to me:
> there are really 2 distinct features at play here (note: when I say
> "member" here, I mean non-static member):
>
> 1) An empty class which takes up no space when it is a member of another
> class.
>
> 2) A Java-style inner class which is directly connected to its owner's
> instance and can therefore access that object's members (as well as its
> own).
>
> When you combine these two in one type, you get what you call "inline
> classes", when nested in a class). So "inline classes" in your definition
> that aren't nested would just be empty classes.
I follow what you're saying. I'm not sure I'm convinced that a change is
warranted, or if so, what would be a better approach.
For the sake of continuing discussion, let me try to rephrase what we want:
1) An empty class which takes up no space when it is a member of another
class.
2) An "inner class" which can access its containing class's members with
no runtime cost in either storage or indirection.
3) An "inner class" which can access its containing class's members and
also has members of its own.
Addressing (3) first, there are two flavors of this as I see it:
3a) The inner class is unique. Parent access can therefore be done using
a constant pointer offset.
3b) The inner class is not unique. The inner class must therefore either
i) store, in addition to its other members, a pointer to the containing
class (be that as a real pointer, or as an offset), or else ii) must be
made unique by means of the type being templated on the offset to the
containing class, thus writing different code for each instance of the
inner class.
Now, we can achieve all of the above already, although the code is
potentially very ugly, so a proposal to make those easier is strictly a
convenience; it doesn't give us anything that can't already be done.
With respect to (3b), the downside of (3bi) of course is that it
increased the required storage and adds an indirection that can't be
optimized away (relocation can still be trivial if the location of the
parent is stored as an offset rather than a complete pointer), while
(3bii) can't be used for an array of such members.
That being the case, I'm not entirely convinced that it doesn't make
sense to focus on (1) and (2), which is what the "inline class" idea as
I have presented it provides. Moreover, I don't believe that I have
precluded (3) from being added to the feature at a later time; in fact,
I have specifically made mention to it in my Future Direction section.
> By separating these concepts, you allow users to pick the functionality
> they want to use. If they find it useful for a Java-style nested class to
> have members or virtuals or whatever, why should you want to explicitly
> prevent that? Why build that limitation into the system?
I have no problem extending the feature to support that; I just prefer
to focus initially on the other use cases. I don't think that the design
precludes being extended to add this support in the future. If it does,
I would consider that a defect in the idea that should be resolved. Do
you see any such issues?
> Similarly, if they find it useful to have a class that doesn't take
> up room that *isn't* a Java-style inner class, again, why should you
> want to stop them?
This seems less relevant to me. You have access to the containing
class's members. If you don't need that, just don't use it. What's the
problem here?
> What I find that I don't like about the proposal currently is that `inline
> class` means two different things in different contexts. Outside of a class
> definition, it means "empty type". Inside of one, it means "empty type that
> can access the surrounding object's innards".
I can understand that. I'm certainly open to tweaking the syntax to make
a syntactic distinction between a FIC and a CIC. (As I've said before,
the current suggested syntax is something of a convenience and I'm not
wedded to it.)
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Wed, 2 Sep 2015 09:13:10 -0700 (PDT)
Raw View
------=_Part_316_97952159.1441210391082
Content-Type: multipart/alternative;
boundary="----=_Part_317_1765299577.1441210391082"
------=_Part_317_1765299577.1441210391082
Content-Type: text/plain; charset=UTF-8
On Wednesday, September 2, 2015 at 11:18:18 AM UTC-4, Matthew Woehlke wrote:
>
> On 2015-09-02 10:33, Nicol Bolas wrote:
> > And `sizeof` is no excuse. `sizeof` for an inline class should simply be
> > what you get for the size of *any* empty class.
>
> Here's the problem:
>
> inline class Foo { ... };
> struct Dog { Foo foo; ... };
> struct Cat { Foo foo; ... };
>
> static_assert(sizeof(Dog::foo) == sizeof(Dog));
> static_assert(sizeof(Cat::foo) == sizeof(Cat));
> // what is sizeof(Foo)?
>
> I guess what you are suggesting is that the size of a FIC should be the
> size of 'class {}'? (Should we explicitly say that a CIC member written
> as a FIC type is actually a different type? I.e. in the above,
> decltype(Dog::foo) != decltype(Foo)? If implemented as implicit
> subclassing, I think this would be okay, since the sizeof an instance
> already can change depending on if the visible type is a subclass or
> base class. in a sense we would be codifying EBO for this case.)
>
Yes, that's the whole idea; you simply apply EBO logic to members.
It's also why I would prefer that the empty optimization be split from the
rest of the CIC restrictions.
Just think about FICs for a moment. The only *necessary* difference between
an FIC and a regular empty class is that FICs don't take up space in the
class. All other restrictions are only *needed* for CICs.
Think about it. Is there something functionally wrong or difficult to
implement if you declaring a stack variable of an FIC type? No; it's no
different from declaring a stack variable of any empty class type. After
all, the `this` pointer for FICs doesn't get any of the special logic that
CICs get. So why create that restriction? Same goes for any other use of
FICs that your current proposal restricts, relative to other types.
If you have FICs as simply empty classes, with all of the rights and
privileges of *any* type, you can still put whatever restrictions you need
on CICs. But those restrictions need not apply to FICs.
At which point, it becomes a bit difficult to justify calling them all
`inline classes`. There are two separate categories of thing: empty classes
and nested inline classes.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_317_1765299577.1441210391082
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Wednesday, September 2, 2015 at 11:18:18 AM UTC-4, Matthew Woehlke wrote=
:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bo=
rder-left: 1px #ccc solid;padding-left: 1ex;">On 2015-09-02 10:33, Nicol Bo=
las wrote:
<br>> And `sizeof` is no excuse. `sizeof` for an inline class should sim=
ply be=20
<br>> what you get for the size of *any* empty class.
<br>
<br>Here's the problem:
<br>
<br>=C2=A0 inline class Foo { ... };
<br>=C2=A0 struct Dog { Foo foo; ... };
<br>=C2=A0 struct Cat { Foo foo; ... };
<br>
<br>=C2=A0 static_assert(sizeof(Dog::foo) =3D=3D sizeof(Dog));
<br>=C2=A0 static_assert(sizeof(Cat::foo) =3D=3D sizeof(Cat));
<br>=C2=A0 // what is sizeof(Foo)?<br></blockquote><blockquote class=3D"gma=
il_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid=
;padding-left: 1ex;">
<br>I guess what you are suggesting is that the size of a FIC should be the
<br>size of 'class {}'? (Should we explicitly say that a CIC member=
written
<br>as a FIC type is actually a different type? I.e. in the above,
<br>decltype(Dog::foo) !=3D decltype(Foo)? If implemented as implicit
<br>subclassing, I think this would be okay, since the sizeof an instance
<br>already can change depending on if the visible type is a subclass or
<br>base class. in a sense we would be codifying EBO for this case.)<br></b=
lockquote><div><br>Yes, that's the whole idea; you simply apply EBO log=
ic to members.<br><br>It's also why I would prefer that the empty optim=
ization be split from the rest of the CIC restrictions.<br><br>Just think a=
bout FICs for a moment. The only <i>necessary</i> difference between an FIC=
and a regular empty class is that FICs don't take up space in the clas=
s. All other restrictions are only <i>needed</i> for CICs.<br><br>Think abo=
ut it. Is there something functionally wrong or difficult to implement if y=
ou declaring a stack variable of an FIC type? No; it's no different fro=
m declaring a stack variable of any empty class type. After all, the `this`=
pointer for FICs doesn't get any of the special logic that CICs get. S=
o why create that restriction? Same goes for any other use of FICs that you=
r current proposal restricts, relative to other types.<br><br>If you have F=
ICs as simply empty classes, with all of the rights and privileges of <i>an=
y</i> type, you can still put whatever restrictions you need on CICs. But t=
hose restrictions need not apply to FICs.<br><br>At which point, it becomes=
a bit difficult to justify calling them all `inline classes`. There are tw=
o separate categories of thing: empty classes and nested inline classes.<br=
></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_317_1765299577.1441210391082--
------=_Part_316_97952159.1441210391082--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Wed, 02 Sep 2015 12:10:51 -0400
Raw View
This is a multi-part message in MIME format.
--------------000501030305070404070209
Content-Type: text/plain; charset=UTF-8
Updated version attached. The prohibition on naming a FIC type has been
removed. I added wording to specify that a template inline class member
is permitted (this would apply only to empty inline classes if we later
add support for non-empty ones) and added partial member "function"
specialization as a use case. I also added some examples.
Open issues:
- What are some use cases for a zero-storage member?
- Do we allow taking the size of a FIC? If yes, what does that mean
w.r.t. taking the size of the same type name as a CIC member? (Is it
truly the same type name at that point? Or does implicit subclassing
happen?)
Feedback appreciated.
Thanks,
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--------------000501030305070404070209
Content-Type: text/prs.fallenstein.rst;
name="inline-class.rst"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="inline-class.rst"
... note::
For the sake of brevity, the term "class" is used throughout most of
this document. As always, the difference between a ``class`` and a
``struct`` is default access protection. Unless specifically stated
otherwise, all instances of either "struct" or "class" should be understood
to mean "class or struct".
Overview
========
This proposal introduces the concept of an "inline class". An inline class is
a special type which is not an object in the normal sense and has no storage.
The use of inline classes is to provide a zero overhead "proxy" for an object,
allowing access to all or part of the object using a modified interface.
Rationale
=========
Users have repeatedly asked for properties to be added to C++. The general
consensus has been that these are too much of a niche feature to justify
expending precious syntax on a first class implementation. Additionally, a
"fully native" property support must make a trade-off between functionality
and simplicity, and makes limited reuse of existing language features. While a
purely library solution is possible, there are various issues that come up in
such an approach, such as back references to the containing class, and how to
specify the get/set methods, that make such an implementation less than
optimal.
Besides properties, there is also a desire to be able to add zero storage
members to types.
A third possible use case is partial specialization of template "member
functions". Providing a templated inline class member, where the inline class
is effectively a functor, allows the member "function" to be specialized by
specializing the inline class in the same manner as specializing a traditional
class.
Lastly, the ability to access different "presentations" of a class is valuable
in some cases. A good example is a color class which internally stores only RGB
values, but offers interfaces to manipulate the color using a variety of color
spaces, especially where multiple such spaces have overlapping component names
(e.g. HSL, HSV, HCY). Inline classes provide a mechanism for retrieving a zero
overhead interface that allows the user to work with a class "as an X", where
the underlying class supports multiple "X"s.
Description
===========
An inline class provides a storage free interface (and is not itself an
"object" in the usual sense). As a result, an inline class may not have
non-static data members, virtual members, or virtual bases, but may have member
functions and static data members in the usual manner. Additionally, an inline
class may have members that are themselves inline classes.
An inline class may not be directly constructed, destroyed, or copied (although
an inline class may define a copy/move assignment operator), and in particular
has no constructors or destructor. Inline classes have no role in the
construction or destruction of concrete (i.e. non-inline) classes that contain
them, as they have no storage to be constructed, copied, or destroyed.
Similarly, the presence of inline class members in a concrete class does not
affect the layout of the Containing Concrete Class (CCC) in any manner.
While the typical use of an inline class is as part of a containing class, it
is also permitted to define Freestanding Inline Classes (FIC's), which may be
used generically inside of concrete classes and/or as base classes of Contained
Inline Classes (CIC's; that is, inline classes that are members |--| whether
directly or indirectly |--| of a concrete class). It is illegal to take the
size (``sizeof``) of a FIC. The size of a CIC is equal to the size of its CCC.
Contained Inline Classes may be named as types and stored or passed either by
reference or by value. The address of a CIC is bitwise equal to the address of
the CCC that contains it. The actual type name may be untypable, as in the case
of anonymous inline classes, or the type name of an inline class member of a
FIC type. The actual type of a FIC used as a member of a concrete class is
unique to the CCC (conceptually, the compiler generates a new copy of the FIC
type for every unique CCC which contains a member of such type). Otherwise, the
type name of a CIC is the same as a contained traditional (concrete) class. For
example:
inline class Free { ... };
class Foo
{
Free x;
inline class { ... } y;
inline class Bar { ... } z;
};
Foo foo;
In the above, the type name of ``foo.z`` is ``Foo::Bar``, while the type names
of ``foo.x`` and ``foo.y`` are untypable.
A CIC reference or pointer, including its ``this`` pointer, may be used to
access the members of the containing class(es) as if the inline class was
derived (using ``protected`` access) from its containing class. (In case of
nested inline classes, the behavior is as if the innermost inline class derives
from its immediately containing class, which in turn derives from its own
immediate containing class, and so on up to the CCC.) Member shadowing follows
the usual rules per the above "as if derived from" logic, and shadowed members
may be accessed via qualification in like manner. A pointer to an inline class
may be ``static_cast``\ ed to a class type which derives from the type of the
original pointer.
As with concrete classes, inline classes may be templated. Moreover, an inline
class *member* may be templated. This is possible for inline class members,
unlike for regular members, because the inline class member has no associated
storage.
An inline class may be forward declared. An undefined (that is, declared
but not yet defined) inline class may additionally be used as a member of
another class (inline or concrete); this is possible since the inline class
member has no effect on the storage or layout of the containing class.
A concrete class may inherit from an inline class. Doing so causes the inline
base class to be treated as if it were a concrete class for the purpose of
defining the derived class.
A type trait std::is_inline_type shall be introduced to detect if a typename is
an inline class.
Examples
========
Implement a property without library support
--------------------------------------------
class Foo
{
public:
inline class
{
operator int() const
{ return m_value; }
auto operator=(int new_value)
{ m_value = new_value; return *this; }
} value;
private:
int m_value = 0;
};
Foo foo;
int old = foo.value;
foo.value = 5;
Implement a property using std::property
----------------------------------------
class Bar
{
public:
inline class Value : public std::property<Value>
{
int get() const { return m_value; }
auto set(int new_value) { m_value = new_value; return *this; }
} value;
private:
int m_value = 0;
};
Bar bar;
bar.value += 3;
Implement a partial specialization of a member "function"
---------------------------------------------------------
class WhatAmI
{
public:
template <typename T> inline class IsIntegral
{
bool operator()() { return false; }
};
template <typename T> static IsIntegral<T> is_integral;
};
template <typename T>
inline class WhatAmI::IsIntegral<T, enable_if<is_integral<T>>::type>
{
bool operator()() { return true; }
};
assert(WhatAmI::is_integral<long>() == true);
assert(WhatAmI::is_integral<float>() == false);
Although this example uses a static inline class member, this is not required.
A more complicated example might also provide different overloads depending on
the template type.
Implement a type with multiple interfaces
-----------------------------------------
class Color
{
public:
inline class { /* ... */ } rgb;
inline class { /* ... */ } hsl;
inline class { /* ... */ } hsv;
inline class { /* ... */ } hcy;
inline class { /* ... */ } yuv;
private:
int _r, _g, _b;
};
Color c1 = /* ... */;
float hsl_h = c1.hsl.saturation;
float hsv_h = c1.hsv.saturation;
Color c2 = /* ... */;
cout << c2.hcy.luma << std::endl;
cout << c2.yuv.luma << std::endl;
auto c3 = c1.rgb * c2.rgb;
auto c4 = c1.yuv * c2.yuv;
In this example, we demonstrate the use of inline classes to provide targeted
interfaces to a type. Each interface allows interacting with the object in a
different manner, and isolates the exposed methods to those relevant to a
particular interface. It is also possible to have members of the same name in
different interfaces which have different functions or values.
While similar functionality is possible by prefixing each possible operation
with the interface name, use of inline class interfaces provides better
compartmentalization and makes for a less monolithic API. (It also allows
per-interface operator overloading, as shown above.)
Discussion
==========
I have chosen the terminology "inline class" to indicate an entity that exists
inside of a concrete class ("inline") without its own dedicated storage, which
otherwise in many ways resembles a class, and because this uses a sequence of
existing keywords which is not currently legal, thus avoiding any potential
change in behavior of existing code. This terminology is, however, contested,
and may not be optimal. Alternate syntax which provides the same functionality
would be acceptable, and may even be preferable; the author does not, however,
have an alternate syntax suggestion at this time.
Because a CIC's address equals the CCC address, it is possible for a concrete
class to have several inline class members whose addresses are equal to that of
the concrete class instance. The author believes that this does not present a
problem; inline classes are conceptually similar to downcasts, except that the
various types in this case would be siblings rather than links in an ancestral
chain. For similar reasons, the author does not expect that inline classes
would create issues with type punning / aliasing.
Since an inline class is not copyable (by value), attempting to pass an inline
class by value (most likely, this would be through a templated parameter type)
would result in an error. Since this is obviously undesirable for properties,
one solution is that inline classes specifically should attempt to invoke a
conversion operator in such case; where multiple conversion operators are
present, the resulting program would naturally be ill-formed. This issue
however can occur also with non-inline classes, and so it may be desirable to
address the issue separately in a way that works for both inline and concrete
classes to allow an implicit value conversion.
Future Direction
================
As will be illustrated in `Appendix I`_, enabling a full suite of arithmetic
operators for a property class requires a) that the user explicitly define
every operator to be supported, or else b) call upon a template base class
using CRTP\ [#]_. This requires that the inline class type be named rather than
anonymous. In general, the requirement in CRTP to reiterate the name of the
derived class violates the DRY\ [#]_ principle, and as such, may be open to
improvement. Such a change should be targeted at all uses of CRTP, not just as
it pertains to inline classes, and therefore is most appropriately addressed in
an orthogonal manner.
Various individuals have expressed a desire to support non-empty inline
classes. Since this introduces a number of additional issues and is only
possible in limited situations (or with additional "magic", such as implicit
type replication), and since the author feels that such support provides only
limited value, the author proposes that any such support should be introduced
as a follow-on proposal, rather than attempting to incorporate it into the
initial proposal.
Acknowledgments
===============
The author wishes to thank Nicol Bolas, Edward Catmur, Miro Knejp and Thiago
Macieira for participating in the discussion and helping to hash out the many
issues that must be addressed, and also everyone who has persisted in asking
for properties in the face of steadfast resistance.
Appendix I
==========
While one intended use case of this feature is to support property-like
constructs in a clean manner, a typical use case still requires the user to
overload every arithmetic operator. This is in fact not a new problem, but
exists for any user defined value-like types, and is already addressed by some
libraries (e.g. ``boost::less_than_comparable``). The syntax used to create
properties via inline classes is also different from existing practice and may
be considered esoteric to novice users. To alleviate these issues, the
following library classes are proposed:
template <typename T, typename Base> inline struct arithmetic
{
Base& operator+=(T const& value)
{
auto& self = *(Base*)(this);
self = self + value;
return *self;
}
// ...similar for -=, *=, etc.
}
template <typename Prop> inline struct property
: arithmetic<decltype((Prop)(nullptr)->get()), Prop>
{
using Type = decltype((Prop)(nullptr)->get());
operator Type() const { return get(); }
Prop& operator=(Type const& value)
{
auto& self = *(Prop*)(this);
return self.set(value);
}
Type get() const
{
auto& self = *(Prop*)(this);
return self.get();
}
property<Prop>& set(Type const& value)
{
*this = value;
return this;
}
}
The provided definitions are meant to be illustrative, not normative. The
names, likewise, are suggestions and are open to change. Additionally, the use
of ``const&`` to accept values should be predicated on the efficiency of doing
so.
... |--| unicode:: U+2014
... [#] `Curiously Recurring Template Pattern <https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_
... [#] `Don't Repeat Yourself <https://en.wikipedia.org/wiki/Don't_repeat_yourself>`_
--------------000501030305070404070209--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Wed, 02 Sep 2015 12:36:06 -0400
Raw View
On 2015-09-02 12:13, Nicol Bolas wrote:
> On Wednesday, September 2, 2015 at 11:18:18 AM UTC-4, Matthew Woehlke wrote:
>> I guess what you are suggesting is that the size of a FIC should be the
>> size of 'class {}'? (Should we explicitly say that a CIC member written
>> as a FIC type is actually a different type? I.e. in the above,
>> decltype(Dog::foo) != decltype(Foo)? If implemented as implicit
>> subclassing, I think this would be okay, since the sizeof an instance
>> already can change depending on if the visible type is a subclass or
>> base class. in a sense we would be codifying EBO for this case.)
>
> Yes, that's the whole idea; you simply apply EBO logic to members.
>
> It's also why I would prefer that the empty optimization be split from the
> rest of the CIC restrictions.
>
> Just think about FICs for a moment. The only *necessary* difference between
> an FIC and a regular empty class is that FICs don't take up space in the
> class. All other restrictions are only *needed* for CICs.
>
> Think about it. Is there something functionally wrong or difficult to
> implement if you declaring a stack variable of an FIC type? No; it's no
> different from declaring a stack variable of any empty class type. After
> all, the `this` pointer for FICs doesn't get any of the special logic that
> CICs get. So why create that restriction? Same goes for any other use of
> FICs that your current proposal restricts, relative to other types.
Okay. Let's work through the implications of this... a CIC of a
freestanding empty class type has a 'this' bitwise equal to its CCC
'this'. This means that any CIC's are implicitly the first members in a
class, storage-wise, and additionally don't take up storage, so a) may
"overlap" with other CIC members, and b) don't affect the offset of
other non-inline-class members.
Well, okay, IIRC there is no requirement on how the compiler orders
class members anyway, so that part should be no issue. And we've already
discussed the point of multiple members having the same address.
So... I guess I don't see a problem looking at it that way. Do you? Does
anyone else?
> If you have FICs as simply empty classes, with all of the rights and
> privileges of *any* type, you can still put whatever restrictions you need
> on CICs. But those restrictions need not apply to FICs.
>
> At which point, it becomes a bit difficult to justify calling them all
> `inline classes`. There are two separate categories of thing: empty classes
> and nested inline classes.
At which point, they become inline *members* :-). Although at that point
I'm less convinced that "inline" is the correct term, plus it is much
more likely to conflict with the inline variables proposal. Suggestions?
At any rate, this would imply that an inline member can be any type, so
long as the type is empty (which, practically speaking, means an empty
class). Moreover, it means these are equivalent:
class Empty {};
class Dog { inline Empty member; }
class Cat { inline class {} member; }
class Cow { class Empty {}; inline Empty member; }
(Note that the 'inline' - or other keyword - is now required before the
type name of an "inline member".)
This also makes it more palatable to me to separate EMO and inner
classes. Would there be a problem allowing 'inner' as a contextual
keyword if followed by 'class' or 'struct'? There is still an issue at
the proposal level, however, as the two remain intertwined; i.e. inline
inner classes need additional stipulations that depend on both features.
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Wed, 2 Sep 2015 10:09:04 -0700 (PDT)
Raw View
------=_Part_730_1732227449.1441213744936
Content-Type: multipart/alternative;
boundary="----=_Part_731_1125199928.1441213744937"
------=_Part_731_1125199928.1441213744937
Content-Type: text/plain; charset=UTF-8
On Wednesday, September 2, 2015 at 12:36:20 PM UTC-4, Matthew Woehlke wrote:
>
> On 2015-09-02 12:13, Nicol Bolas wrote:
> > On Wednesday, September 2, 2015 at 11:18:18 AM UTC-4, Matthew Woehlke
> wrote:
> At which point, they become inline *members* :-). Although at that point
> I'm less convinced that "inline" is the correct term, plus it is much
> more likely to conflict with the inline variables proposal. Suggestions?
>
> At any rate, this would imply that an inline member can be any type, so
> long as the type is empty (which, practically speaking, means an empty
> class). Moreover, it means these are equivalent:
>
> class Empty {};
> class Dog { inline Empty member; }
>
> class Cat { inline class {} member; }
>
> class Cow { class Empty {}; inline Empty member; }
>
> (Note that the 'inline' - or other keyword - is now required before the
> type name of an "inline member".)
>
I think the empty decoration (whatever that may be) would work better on
the declaration/definition for the class rather than the use of the class.
That way, if I pass a truly empty class to a template, it doesn't have to
put `inline` in its member definition to get the space optimization.
It should be a property of the type, not its particular use.
This also makes it more palatable to me to separate EMO and inner
> classes. Would there be a problem allowing 'inner' as a contextual
> keyword if followed by 'class' or 'struct'?
Syntactically, that requires look-ahead for the compiler to make sense of
it. Contextual keywords are identifiers except in the specific context that
accepts them. And in the two cases of contextual keywords, the context
comes first.
So using contextual keywords, it would probably be best to do it like
`final` (since the grammar already has space for it):
class outer
{
class nest inner
{
};
};
However, that posts a syntactical issue for nameless inner classes:
class outer
{
class inner //which is it?
{
};
};
Syntactically, `inline class` probably works with the fewest issues.
There is still an issue at
> the proposal level, however, as the two remain intertwined; i.e. inline
> inner classes need additional stipulations that depend on both features.
>
True. But so long as empty members can be made to work, I don't think there
are any language issues with inner classes, whether they are empty or not.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_731_1125199928.1441213744937
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<br><br>On Wednesday, September 2, 2015 at 12:36:20 PM UTC-4, Matthew Woehl=
ke wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: =
0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">On 2015-09-02 12:13, =
Nicol Bolas wrote:
<br>> On Wednesday, September 2, 2015 at 11:18:18 AM UTC-4, Matthew Woeh=
lke wrote:
<br>At which point, they become inline *members* :-). Although at that poin=
t
<br>I'm less convinced that "inline" is the correct term, plu=
s it is much
<br>more likely to conflict with the inline variables proposal. Suggestions=
?
<br>
<br>At any rate, this would imply that an inline member can be any type, so
<br>long as the type is empty (which, practically speaking, means an empty
<br>class). Moreover, it means these are equivalent:
<br>
<br>=C2=A0 class Empty {};
<br>=C2=A0 class Dog { inline Empty member; }
<br>
<br>=C2=A0 class Cat { inline class {} member; }
<br>
<br>=C2=A0 class Cow { class Empty {}; inline Empty member; }
<br>
<br>(Note that the 'inline' - or other keyword - is now required be=
fore the
<br>type name of an "inline member".)<br></blockquote><div><br>I =
think the empty decoration (whatever that may be) would work better on the =
declaration/definition for the class rather than the use of the class. That=
way, if I pass a truly empty class to a template, it doesn't have to p=
ut `inline` in its member definition to get the space optimization.<br><br>=
It should be a property of the type, not its particular use.<br><br><br></d=
iv><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;=
border-left: 1px #ccc solid;padding-left: 1ex;">
This also makes it more palatable to me to separate EMO and inner
<br>classes. Would there be a problem allowing 'inner' as a context=
ual
<br>keyword if followed by 'class' or 'struct'?</blockquote=
><div><br>Syntactically, that requires look-ahead for the compiler to make =
sense of it. Contextual keywords are identifiers except in the specific con=
text that accepts them. And in the two cases of contextual keywords, the co=
ntext comes first.<br><br>So using contextual keywords, it would probably b=
e best to do it like `final` (since the grammar already has space for it):<=
br><br><div style=3D"text-align: left;"><div class=3D"prettyprint" style=3D=
"background-color: rgb(250, 250, 250); border-color: rgb(187, 187, 187); bo=
rder-style: solid; border-width: 1px; word-wrap: break-word;"><code class=
=3D"prettyprint"><div class=3D"subprettyprint"><span style=3D"color: #008;"=
class=3D"styled-by-prettify">class</span><span style=3D"color: #000;" clas=
s=3D"styled-by-prettify"> outer<br></span><span style=3D"color: #660;" clas=
s=3D"styled-by-prettify">{</span><span style=3D"color: #000;" class=3D"styl=
ed-by-prettify"><br>=C2=A0 </span><span style=3D"color: #008;" class=3D"sty=
led-by-prettify">class</span><span style=3D"color: #000;" class=3D"styled-b=
y-prettify"> nest inner<br>=C2=A0 </span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">{</span><span style=3D"color: #000;" class=3D"style=
d-by-prettify"><br>=C2=A0 </span><span style=3D"color: #660;" class=3D"styl=
ed-by-prettify">};</span><span style=3D"color: #000;" class=3D"styled-by-pr=
ettify"><br></span><span style=3D"color: #660;" class=3D"styled-by-prettify=
">};</span></div></code></div></div><br>However, that posts a syntactical i=
ssue for nameless inner classes:<br><br><div style=3D"text-align: left;"><d=
iv class=3D"prettyprint" style=3D"background-color: rgb(250, 250, 250); bor=
der-color: rgb(187, 187, 187); border-style: solid; border-width: 1px; word=
-wrap: break-word;"><code class=3D"prettyprint"><div class=3D"subprettyprin=
t"><span style=3D"color: #008;" class=3D"styled-by-prettify">class</span><s=
pan style=3D"color: #000;" class=3D"styled-by-prettify"> outer<br></span><s=
pan style=3D"color: #660;" class=3D"styled-by-prettify">{</span><span style=
=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 </span><span styl=
e=3D"color: #008;" class=3D"styled-by-prettify">class</span><span style=3D"=
color: #000;" class=3D"styled-by-prettify"> inner </span><span style=3D"col=
or: #800;" class=3D"styled-by-prettify">//which is it?</span><span style=3D=
"color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 </span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">{</span><span style=3D"color=
: #000;" class=3D"styled-by-prettify"><br>=C2=A0 </span><span style=3D"colo=
r: #660;" class=3D"styled-by-prettify">};</span><span style=3D"color: #000;=
" class=3D"styled-by-prettify"><br></span><span style=3D"color: #660;" clas=
s=3D"styled-by-prettify">};</span></div></code></div></div><br>Syntacticall=
y, `inline class` probably works with the fewest issues.<br><br></div><bloc=
kquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-l=
eft: 1px #ccc solid;padding-left: 1ex;"> There is still an issue at
<br>the proposal level, however, as the two remain intertwined; i.e. inline
<br>inner classes need additional stipulations that depend on both features=
..<br></blockquote><div><br>True. But so long as empty members can be made t=
o work, I don't think there are any language issues with inner classes,=
whether they are empty or not.<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_731_1125199928.1441213744937--
------=_Part_730_1732227449.1441213744936--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Wed, 02 Sep 2015 14:23:07 -0400
Raw View
On 2015-09-02 13:09, Nicol Bolas wrote:
> I think the empty decoration (whatever that may be) would work better on
> the declaration/definition for the class rather than the use of the class.
I have mixed feelings there. Mostly, I think, because on a non-CIC
member FIC, the keyword would be superfluous in said context. Placing it
on the member declaration doesn't have this problem. OTOH...
> That way, if I pass a truly empty class to a template, it doesn't have to
> put `inline` in its member definition to get the space optimization.
....this is also a good point.
> On Wednesday, September 2, 2015 at 12:36:20 PM UTC-4, Matthew Woehlke wrote:
>> This also makes it more palatable to me to separate EMO and inner
>> classes. Would there be a problem allowing 'inner' as a contextual
>> keyword if followed by 'class' or 'struct'?
>
> Syntactically, that requires look-ahead for the compiler to make sense of
> it. Contextual keywords are identifiers except in the specific context that
> accepts them. And in the two cases of contextual keywords, the context
> comes first.
>
> So using contextual keywords, it would probably be best to do it like
> `final` (since the grammar already has space for it):
>
> class outer
> {
> class nest inner
> {
> };
> };
>
> However, that posts a syntactical issue for nameless inner classes:
>
> class outer
> {
> class inner //which is it?
> {
> };
> };
Off the cuff... would this be a too-ridiculous solution for inner classes?
class outer
{
class inner : protected this
{
}
}
....or this?
class outer
{
class inner : extern outer
{
}
}
(Using 'extern' to disambiguate that we mean an outer class; 'extern'
could either imply 'protected', or we could use the usual default and
permit an explicit access specifier in addition. For that matter, the
class name could be omitted.)
....or we could combine the two and use 'extern this'. I think maybe I
like that better; it could be seen as "my 'this' is external to my
definition", which is roughly one way of thinking about an inner class.
>> There is still an issue at the proposal level, however, as the two
>> remain intertwined; i.e. inline inner classes need additional
>> stipulations that depend on both features.
>
> True. But so long as empty members can be made to work, I don't think there
> are any language issues with inner classes, whether they are empty or not.
The trouble is that non-empty inner classes must either a) store as a
member, or b) encode into the generated code via "magic" type
replication, the offset to the parent class in order to correctly access
the members. (I won't rehash the pros and cons of each approach.)
However, we specifically want empty inner classes to always use the
second option (and implicitly, to have the same code for all copies,
since the offset in all cases is 0). Expressing that (potentially, i.e.
if we don't go with option (b)) requires that the inner class proposal
is aware of empty members.
Note that I'm less in favor of (b) because it involves magical type
creation, and because it precludes arrays of inner classes. (Note also,
I'm assuming offsets, because offsets don't preclude relocation and/or
trivial copying.)
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Thu, 3 Sep 2015 07:45:32 -0700 (PDT)
Raw View
------=_Part_757_1750433621.1441291532689
Content-Type: multipart/alternative;
boundary="----=_Part_758_543821446.1441291532689"
------=_Part_758_543821446.1441291532689
Content-Type: text/plain; charset=UTF-8
On Wednesday, September 2, 2015 at 2:23:22 PM UTC-4, Matthew Woehlke wrote:
>
> On 2015-09-02 13:09, Nicol Bolas wrote:
> > On Wednesday, September 2, 2015 at 12:36:20 PM UTC-4, Matthew Woehlke
> wrote:
> >> This also makes it more palatable to me to separate EMO and inner
> >> classes. Would there be a problem allowing 'inner' as a contextual
> >> keyword if followed by 'class' or 'struct'?
> >
> > Syntactically, that requires look-ahead for the compiler to make sense
> of
> > it. Contextual keywords are identifiers except in the specific context
> that
> > accepts them. And in the two cases of contextual keywords, the context
> > comes first.
> >
> > So using contextual keywords, it would probably be best to do it like
> > `final` (since the grammar already has space for it):
> >
> > class outer
> > {
> > class nest inner
> > {
> > };
> > };
> >
> > However, that posts a syntactical issue for nameless inner classes:
> >
> > class outer
> > {
> > class inner //which is it?
> > {
> > };
> > };
>
> Off the cuff... would this be a too-ridiculous solution for inner classes?
>
> class outer
> {
> class inner : protected this
> {
> }
> }
>
> ...or this?
>
> class outer
> {
> class inner : extern outer
> {
> }
> }
>
> (Using 'extern' to disambiguate that we mean an outer class; 'extern'
> could either imply 'protected', or we could use the usual default and
> permit an explicit access specifier in addition. For that matter, the
> class name could be omitted.)
>
> ...or we could combine the two and use 'extern this'. I think maybe I
> like that better; it could be seen as "my 'this' is external to my
> definition", which is roughly one way of thinking about an inner class.
>
That could work, since `extern` is a keyword. I'm not sure about using the
colon though, since that suggests derived classes. It might be better to
just put the `extern` immediately after the name (if there is a name). So
it would be `class extern this` for anonymous classes.
>> There is still an issue at the proposal level, however, as the two
> >> remain intertwined; i.e. inline inner classes need additional
> >> stipulations that depend on both features.
> >
> > True. But so long as empty members can be made to work, I don't think
> there
> > are any language issues with inner classes, whether they are empty or
> not.
>
> The trouble is that non-empty inner classes must either a) store as a
> member, or b) encode into the generated code via "magic" type
> replication, the offset to the parent class in order to correctly access
> the members.
Actually, the standard does not have to specific explicitly *how* the
compiler deals with it. Just as the standard does not say that classes with
virtual members must have a hidden vtable pointer. The standard allows for
such an implementation by declaring that types with virtuals cannot be
standard layout types and so forth. That gives the implementation the
freedom to add hidden members and play games in the constructor.
The standard can do something similar here. It can require that inner
classes still be trivially copyable (assuming that the inner classes don't
do something that prevents trivial copyability). This prevents the compiler
from using the "hidden `this` pointer" option for inner classes that have
members. The only option this leaves is storing, for each inner class
member, a byte-offset from the inner class member to the owning object.
This is fixed for each member, but it has to be stored in each member,
since you might get a reference/pointer to an inner class member. The data
can still be memcpy'd, so the type is still trivially copyable.
But the standard doesn't have to spell out the specific implementation.
Just the behavior: maintain trivial copyability, but not standard layout.
This should all be implementable, because the compiler knows which inner
class types are empty and which ones are not. So the compiler generates
different code for the two cases. For empty inner class members, there is
no need for an offset (since the pointer value to an empty class member is
always that of the owning object). And for non-empty inner class members,
the offset only breaks standard layout, not trivial copyability.
> (I won't rehash the pros and cons of each approach.)
> However, we specifically want empty inner classes to always use the
> second option (and implicitly, to have the same code for all copies,
> since the offset in all cases is 0). Expressing that (potentially, i.e.
> if we don't go with option (b)) requires that the inner class proposal
> is aware of empty members.
> Note that I'm less in favor of (b) because it involves magical type
> creation, and because it precludes arrays of inner classes. (Note also,
> I'm assuming offsets, because offsets don't preclude relocation and/or
> trivial copying.)
>
Arrays of inner classes are more or less fine, since I'm pretty sure the
compiler can do what is necessary to compute the offsets for each member..
The problem is arrays of *empty* classes (inner or not). In C++, you
basically have to guarantee that this works, for any type:
T arr[5];
T *pArr = arr;
pArr + 3 == &arr[3];
That would require that empty classes put into arrays must take up storage.
The problem then is doing the pointer fixup needed to make the `this`
pointer point to the right memory location. So you can't guarantee that
each empty class instance gets the right pointer.
So if you have a member array of empty types, it *must* take up room.
However, since you used an empty type as a member variable, you clearly
don't want or expect it to take up room. Therefore, member arrays of empty
types (again, inner or not) should simply be forbidden. You can still
allocate arrays of empty types on the stack or heap, but those are always
expected to take up memory.
I don't like this solution, but I don't have a better solution for it
either.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_758_543821446.1441291532689
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Wednesday, September 2, 2015 at 2:23:22 PM UTC-4, Matthew Woehlke wrote:=
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;">On 2015-09-02 13:09, Nicol Bol=
as wrote:
<br>> On Wednesday, September 2, 2015 at 12:36:20 PM UTC-4, Matthew Woeh=
lke wrote:
<br>>> This also makes it more palatable to me to separate EMO and in=
ner=20
<br>>> classes. Would there be a problem allowing 'inner' as =
a contextual=20
<br>>> keyword if followed by 'class' or 'struct'?
<br>>=20
<br>> Syntactically, that requires look-ahead for the compiler to make s=
ense of=20
<br>> it. Contextual keywords are identifiers except in the specific con=
text that=20
<br>> accepts them. And in the two cases of contextual keywords, the con=
text=20
<br>> comes first.
<br>>=20
<br>> So using contextual keywords, it would probably be best to do it l=
ike=20
<br>> `final` (since the grammar already has space for it):
<br>>=20
<br>> class outer
<br>> {
<br>> =C2=A0 class nest inner
<br>> =C2=A0 {
<br>> =C2=A0 };
<br>> };
<br>>=20
<br>> However, that posts a syntactical issue for nameless inner classes=
:
<br>>=20
<br>> class outer
<br>> {
<br>> =C2=A0 class inner //which is it?
<br>> =C2=A0 {
<br>> =C2=A0 };
<br>> };
<br>
<br>Off the cuff... would this be a too-ridiculous solution for inner class=
es?
<br>
<br>=C2=A0 class outer
<br>=C2=A0 {
<br>=C2=A0 =C2=A0 class inner : protected this
<br>=C2=A0 =C2=A0 {
<br>=C2=A0 =C2=A0 }
<br>=C2=A0 }
<br>
<br>...or this?
<br>
<br>=C2=A0 class outer
<br>=C2=A0 {
<br>=C2=A0 =C2=A0 class inner : extern outer
<br>=C2=A0 =C2=A0 {
<br>=C2=A0 =C2=A0 }
<br>=C2=A0 }
<br>
<br>(Using 'extern' to disambiguate that we mean an outer class; &#=
39;extern'
<br>could either imply 'protected', or we could use the usual defau=
lt and
<br>permit an explicit access specifier in addition. For that matter, the
<br>class name could be omitted.)
<br>
<br>...or we could combine the two and use 'extern this'. I think m=
aybe I
<br>like that better; it could be seen as "my 'this' is extern=
al to my
<br>definition", which is roughly one way of thinking about an inner c=
lass.<br></blockquote><div><br>That could work, since `extern` is a keyword=
.. I'm not sure about using the colon though, since that suggests derive=
d classes. It might be better to just put the `extern` immediately after th=
e name (if there is a name). So it would be `class extern this` for anonymo=
us classes.<br><br></div><blockquote class=3D"gmail_quote" style=3D"margin:=
0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
>> There is still an issue at the proposal level, however, as the two
<br>>> remain intertwined; i.e. inline inner classes need additional
<br>>> stipulations that depend on both features.
<br>>=20
<br>> True. But so long as empty members can be made to work, I don'=
t think there=20
<br>> are any language issues with inner classes, whether they are empty=
or not.
<br>
<br>The trouble is that non-empty inner classes must either a) store as a
<br>member, or b) encode into the generated code via "magic" type
<br>replication, the offset to the parent class in order to correctly acces=
s
<br>the members.</blockquote><div><br>Actually, the standard does not have =
to specific explicitly <i>how</i> the compiler deals with it. Just as the s=
tandard does not say that classes with virtual members must have a hidden v=
table pointer. The standard allows for such an implementation by declaring =
that types with virtuals cannot be standard layout types and so forth. That=
gives the implementation the freedom to add hidden members and play games =
in the constructor.<br><br>The standard can do something similar here. It c=
an require that inner classes still be trivially copyable (assuming that th=
e inner classes don't do something that prevents trivial copyability). =
This prevents the compiler from using the "hidden `this` pointer"=
option for inner classes that have members. The only option this leaves is=
storing, for each inner class member, a byte-offset from the inner class m=
ember to the owning object.<br><br>This is fixed for each member, but it ha=
s to be stored in each member, since you might get a reference/pointer to a=
n inner class member. The data can still be memcpy'd, so the type is st=
ill trivially copyable.<br><br>But the standard doesn't have to spell o=
ut the specific implementation. Just the behavior: maintain trivial copyabi=
lity, but not standard layout.<br><br>This should all be implementable, bec=
ause the compiler knows which inner class types are empty and which ones ar=
e not. So the compiler generates different code for the two cases. For empt=
y inner class members, there is no need for an offset (since the pointer va=
lue to an empty class member is always that of the owning object). And for =
non-empty inner class members, the offset only breaks standard layout, not =
trivial copyability.<br>=C2=A0</div><blockquote class=3D"gmail_quote" style=
=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: =
1ex;"> (I won't rehash the pros and cons of each approach.)
<br>However, we specifically want empty inner classes to always use the
<br>second option (and implicitly, to have the same code for all copies,
<br>since the offset in all cases is 0). Expressing that (potentially, i.e.
<br>if we don't go with option (b)) requires that the inner class propo=
sal
<br>is aware of empty members.=C2=A0</blockquote><blockquote class=3D"gmail=
_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;p=
adding-left: 1ex;">
<br>Note that I'm less in favor of (b) because it involves magical type
<br>creation, and because it precludes arrays of inner classes. (Note also,
<br>I'm assuming offsets, because offsets don't preclude relocation=
and/or
<br>trivial copying.)
<br></blockquote><div><br>Arrays of inner classes are more or less fine, si=
nce I'm pretty sure the compiler can do what is necessary to compute th=
e offsets for each member.. The problem is arrays of <i>empty</i> classes (=
inner or not). In C++, you basically have to guarantee that this works, for=
any type:<br><br><div class=3D"prettyprint" style=3D"background-color: rgb=
(250, 250, 250); border-color: rgb(187, 187, 187); border-style: solid; bor=
der-width: 1px; word-wrap: break-word;"><code class=3D"prettyprint"><div cl=
ass=3D"subprettyprint"><span style=3D"color: #000;" class=3D"styled-by-pret=
tify">T arr</span><span style=3D"color: #660;" class=3D"styled-by-prettify"=
>[</span><span style=3D"color: #066;" class=3D"styled-by-prettify">5</span>=
<span style=3D"color: #660;" class=3D"styled-by-prettify">];</span><span st=
yle=3D"color: #000;" class=3D"styled-by-prettify"><br>T </span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">*</span><span style=3D"color=
: #000;" class=3D"styled-by-prettify">pArr </span><span style=3D"color: #66=
0;" class=3D"styled-by-prettify">=3D</span><span style=3D"color: #000;" cla=
ss=3D"styled-by-prettify"> arr</span><span style=3D"color: #660;" class=3D"=
styled-by-prettify">;</span><span style=3D"color: #000;" class=3D"styled-by=
-prettify"><br>pArr </span><span style=3D"color: #660;" class=3D"styled-by-=
prettify">+</span><span style=3D"color: #000;" class=3D"styled-by-prettify"=
> </span><span style=3D"color: #066;" class=3D"styled-by-prettify">3</span>=
<span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span sty=
le=3D"color: #660;" class=3D"styled-by-prettify">=3D=3D</span><span style=
=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color=
: #660;" class=3D"styled-by-prettify">&</span><span style=3D"color: #00=
0;" class=3D"styled-by-prettify">arr</span><span style=3D"color: #660;" cla=
ss=3D"styled-by-prettify">[</span><span style=3D"color: #066;" class=3D"sty=
led-by-prettify">3</span><span style=3D"color: #660;" class=3D"styled-by-pr=
ettify">];</span></div></code></div><br>That would require that empty class=
es put into arrays must take up storage. The problem then is doing the poin=
ter fixup needed to make the `this` pointer point to the right memory locat=
ion. So you can't guarantee that each empty class instance gets the rig=
ht pointer.<br><br>So if you have a member array of empty types, it <i>must=
</i> take up room. However, since you used an empty type as a member variab=
le, you clearly don't want or expect it to take up room. Therefore, mem=
ber arrays of empty types (again, inner or not) should simply be forbidden.=
You can still allocate arrays of empty types on the stack or heap, but tho=
se are always expected to take up memory.<br><br>I don't like this solu=
tion, but I don't have a better solution for it either.<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_758_543821446.1441291532689--
------=_Part_757_1750433621.1441291532689--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Thu, 03 Sep 2015 11:18:44 -0400
Raw View
On 2015-09-03 10:45, Nicol Bolas wrote:
> On Wednesday, September 2, 2015 at 2:23:22 PM UTC-4, Matthew Woehlke wrot=
e:
>> Off the cuff... would this be a too-ridiculous solution for inner classe=
s?=20
>>
>> class outer=20
>> {=20
>> class inner : protected this=20
>> {=20
>> }=20
>> }=20
>>
>> ...or this?=20
>>
>> class outer=20
>> {=20
>> class inner : extern outer=20
>> {=20
>> }=20
>> }=20
>>
>> (Using 'extern' to disambiguate that we mean an outer class; 'extern'=20
>> could either imply 'protected', or we could use the usual default and=20
>> permit an explicit access specifier in addition. For that matter, the=20
>> class name could be omitted.)=20
>>
>> ...or we could combine the two and use 'extern this'. I think maybe I=20
>> like that better; it could be seen as "my 'this' is external to my=20
>> definition", which is roughly one way of thinking about an inner class.
>=20
> That could work, since `extern` is a keyword. I'm not sure about using th=
e=20
> colon though, since that suggests derived classes. It might be better to=
=20
> just put the `extern` immediately after the name (if there is a name). So=
=20
> it would be `class extern this` for anonymous classes.
Okay. For now I'm going to keep the colon; partly because the token
chain seems a little odd to me otherwise, but also because inner classes
*act* like derived classes, at least in terms of access to parent members.
I still consider this a bikeshed, however, i.e. the syntax portion is a
weak proposal that I would be very open to improving.
>> The trouble is that non-empty inner classes must either a) store as a=20
>> member, or b) encode into the generated code via "magic" type=20
>> replication, the offset to the parent class in order to correctly access=
=20
>> the members.
>=20
> Actually, the standard does not have to specific explicitly *how* the=20
> compiler deals with it.
I'm not convinced that is true in this case. If we specify trivial
relocatability (and I think it would be a travesty to do otherwise), we
implicitly preclude storing the complete pointer (vs. an offset). Also,
consider:
struct Outer
{
struct Inner : extern this { ... };
Inner a, b, c[5];
};
I feel like we ought to be consistent across compilers as to whether or
not the above (particularly the array member) is well formed, and
whether decltype(a) =3D=3D decltype(b). By specifying those (either way), w=
e
effectively *have* specified the implementation, even if not explicitly.
Therefore, I think it's important to consider the benefits and drawbacks
of each possible implementation in order to decide how to answer those
questions.
> The only option this leaves is storing, for each inner class=20
> member, a byte-offset from the inner class member to the owning object.
Not true; the compiler *could* generate different code for each member
(I think this was Miro's suggestion originally). This would eliminate
the additional storage and a level of indirection, but would preclude
arrays of inner classes and would make the actual type be different per
member (and likely untypable=C2=B9). I personally am inclined away from tha=
t
approach, but as above, my strongest feeling is that we should have
*some* decision on this point.
(=C2=B9 I don't *think* there has been confusion here, but just to make
certain, what I mean by "untypable" is that the user cannot press
keyboard keys ("type") to form the type name, and must use decltype /
type deduction. That is, "type" as in "press keys on a keyboard", not
"type" as in "(internally) has a type name". Stupid ambiguous English
language ;-).)
> Arrays of inner classes are more or less fine, since I'm pretty sure the=
=20
> compiler can do what is necessary to compute the offsets for each member.=
..=20
Yes, I would also think so...
> The problem is arrays of *empty* classes (inner or not). In C++, you=20
> basically have to guarantee that this works, for any type:
>=20
> T arr[5];
> T *pArr =3D arr;
> pArr + 3 =3D=3D &arr[3];
Good point. OTOH, I can't figure out any way in which an array of empty
entities would be useful... they are all identical, so what is the point
of having more than one of them?
(Of course, if sizeof(empty_type) =3D=3D 0, this would still work ;-), but =
I
know you don't want to go there, and I'm not going to suggest that we do.)
--=20
Matthew
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Thu, 3 Sep 2015 12:21:15 -0700 (PDT)
Raw View
------=_Part_4_2097509987.1441308075720
Content-Type: multipart/alternative;
boundary="----=_Part_5_1492520868.1441308075721"
------=_Part_5_1492520868.1441308075721
Content-Type: text/plain; charset=UTF-8
There may be a bit of a misunderstanding on what I was trying to say.
I'm not saying that discussing the implementation is unimportant. What I
means is that the feature needs to *start* with behavior. That is, what we
want it to do. Then, we start to look at what implementing that behavior
would require from compiler vendors. And in some cases, if it would be
possible at all.
For example, Miro's suggestion is not merely implementation; it is
*behavior* (as well). It says that inner classes are all templates, with an
implicit CRTP use. So each inner class member is a separate type. Such
behavior is apparent and it has consequences for how the feature could be
used.
For example, if you want a function that takes a reference to an inner
class member, you can't do that under his proposal without making this a
template function. Whereas if it were just a concrete class, you could. Is
this behavior we really want?
I don't think so.
By contrast, the question of pointer vs. offset is purely implementation.
So long as the requested behavior (inner classes with state can maintain
trivial copyability) has *some* implementation, and this implementation is
not burdensome on compilers or at runtime, then the question of the
implementation is more or less moot.
So I would say that after we iron out what behavior we want, then we can
look to questions of how it gets implemented, or whether it is
implementable at all. And we can talk about implementations to make sure
that obvious problems can be worked around (which is why I don't suggest
that inner classes with members be standard layout). But we shouldn't
restrict behavior based on implementations unless it is actually
unimplementable.
For example, I would say that inner classes should be as usable as regular
classes except where that use would break the concept. Allocating an inner
class on the stack or heap directly makes no sense, nor does trying to use
an inner class from type A inside a class of type B (though that brings up
a new question of behavior: if B were derived from A, could B create new
instances of A's inner classes?). Placement new also does not make sense on
inner classes (though obviously you can use it on non-inner classes that
contain inner class members).
Inner classes ought to be able to have constructors, and the owning class
should be able to pass parameters to them. The fact that the compiler will
generate some extra code for those constructors is more or less irrelevant.
Inner classes should be able to have copy/move constructors if the user so
desires.
Similarly, NSDM's of inner class types should be as functional as any
variable of any class type, except where that functionality does not make
sense. So getting pointers/references should work, and they should be just
as functional as they would be for a regular class type. However, it makes
no sense for a user to explicitly invoke the copy/move action of an inner
class member, so that can be forbidden. You can copy/move the owning class,
but not an inner class member directly.
On Thursday, September 3, 2015 at 11:18:58 AM UTC-4, Matthew Woehlke wrote:
>
> On 2015-09-03 10:45, Nicol Bolas wrote:
> > On Wednesday, September 2, 2015 at 2:23:22 PM UTC-4, Matthew Woehlke
> wrote:
> > Arrays of inner classes are more or less fine, since I'm pretty sure the
> > compiler can do what is necessary to compute the offsets for each
> member..
>
> Yes, I would also think so...
>
> > The problem is arrays of *empty* classes (inner or not). In C++, you
> > basically have to guarantee that this works, for any type:
> >
> > T arr[5];
> > T *pArr = arr;
> > pArr + 3 == &arr[3];
>
> Good point. OTOH, I can't figure out any way in which an array of empty
> entities would be useful... they are all identical, so what is the point
> of having more than one of them?
>
You might have some template code where the implementation creates a struct
that contains an array of some type.
But this is also a good reason why it's OK to forbid it: because it's
ultimately not very useful even if it were allowed ;)
That being said, we would also need some `std::is_inner_type` to detect
when something is an inner class or not.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_5_1492520868.1441308075721
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">There may be a bit of a misunderstanding on what I was try=
ing to say.<br><br>I'm not saying that discussing the implementation is=
unimportant. What I means is that the feature needs to <i>start</i> with b=
ehavior. That is, what we want it to do. Then, we start to look at what imp=
lementing that behavior would require from compiler vendors. And in some ca=
ses, if it would be possible at all.<br><br>For example, Miro's suggest=
ion is not merely implementation; it is <i>behavior</i> (as well). It says =
that inner classes are all templates, with an implicit CRTP use. So each in=
ner class member is a separate type. Such behavior is apparent and it has c=
onsequences for how the feature could be used.<br><br>For example, if you w=
ant a function that takes a reference to an inner class member, you can'=
;t do that under his proposal without making this a template function. Wher=
eas if it were just a concrete class, you could. Is this behavior we really=
want?<br><br>I don't think so.<br><br>By contrast, the question of poi=
nter vs. offset is purely implementation. So long as the requested behavior=
(inner classes with state can maintain trivial copyability) has <i>some</i=
> implementation, and this implementation is not burdensome on compilers or=
at runtime, then the question of the implementation is more or less moot.<=
br><br>So I would say that after we iron out what behavior we want, then we=
can look to questions of how it gets implemented, or whether it is impleme=
ntable at all. And we can talk about implementations to make sure that obvi=
ous problems can be worked around (which is why I don't suggest that in=
ner classes with members be standard layout). But we shouldn't restrict=
behavior based on implementations unless it is actually unimplementable.<b=
r><br>For example, I would say that inner classes should be as usable as re=
gular classes except where that use would break the concept. Allocating an =
inner class on the stack or heap directly makes no sense, nor does trying t=
o use an inner class from type A inside a class of type B (though that brin=
gs up a new question of behavior: if B were derived from A, could B create =
new instances of A's inner classes?). Placement new also does not make =
sense on inner classes (though obviously you can use it on non-inner classe=
s that contain inner class members).<br><br>Inner classes ought to be able =
to have constructors, and the owning class should be able to pass parameter=
s to them. The fact that the compiler will generate some extra code for tho=
se constructors is more or less irrelevant. Inner classes should be able to=
have copy/move constructors if the user so desires.<br><br>Similarly, NSDM=
's of inner class types should be as functional as any variable of any =
class type, except where that functionality does not make sense. So getting=
pointers/references should work, and they should be just as functional as =
they would be for a regular class type. However, it makes no sense for a us=
er to explicitly invoke the copy/move action of an inner class member, so t=
hat can be forbidden. You can copy/move the owning class, but not an inner =
class member directly.<br><br>On Thursday, September 3, 2015 at 11:18:58 AM=
UTC-4, Matthew Woehlke wrote:<blockquote class=3D"gmail_quote" style=3D"ma=
rgin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">=
On 2015-09-03 10:45, Nicol Bolas wrote:
<br>> On Wednesday, September 2, 2015 at 2:23:22 PM UTC-4, Matthew Woehl=
ke wrote:
<br>
> Arrays of inner classes are more or less fine, since I'm pretty su=
re the=20
<br>> compiler can do what is necessary to compute the offsets for each =
member..=20
<br>
<br>Yes, I would also think so...
<br>
<br>> The problem is arrays of *empty* classes (inner or not). In C++, y=
ou=20
<br>> basically have to guarantee that this works, for any type:
<br>>=20
<br>> T arr[5];
<br>> T *pArr =3D arr;
<br>> pArr + 3 =3D=3D &arr[3];
<br>
<br>Good point. OTOH, I can't figure out any way in which an array of e=
mpty
<br>entities would be useful... they are all identical, so what is the poin=
t
<br>of having more than one of them?
<br></blockquote><div><br>You might have some template code where the imple=
mentation creates a struct that contains an array of some type.<br><br>But =
this is also a good reason why it's OK to forbid it: because it's u=
ltimately not very useful even if it were allowed ;)<br><br>That being said=
, we would also need some `std::is_inner_type` to detect when something is =
an inner class or not.</div></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_5_1492520868.1441308075721--
------=_Part_4_2097509987.1441308075720--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Thu, 03 Sep 2015 17:10:40 -0400
Raw View
On 2015-09-03 15:21, Nicol Bolas wrote:
> There may be a bit of a misunderstanding on what I was trying to say.
Maybe, maybe not... my point was merely that we need to discuss the
matter (whether starting from behavior or from implementation). I wasn't
getting a clear sense of your feelings in that respect from the previous
post. Whereas...
> For example, Miro's suggestion is not merely implementation; it is
> *behavior* (as well). It says that inner classes are all templates, with an
> implicit CRTP use. So each inner class member is a separate type. Such
> behavior is apparent and it has consequences for how the feature could be
> used.
>
> For example, if you want a function that takes a reference to an inner
> class member, you can't do that under his proposal without making this a
> template function. Whereas if it were just a concrete class, you could. Is
> this behavior we really want?
>
> I don't think so.
....this is much clearer; thanks :-).
I also tend to agree.
I think we agree that, behaviorally, an inner class should be trivially
copyable, and that all members of a particular inner class type should
actually be of that type, i.e. no hidden/implicit/magic templates.
I think I would also refrain from encouraging any implicit optimization
in the case of non-inline inner classes. The trouble here is that doing
so means that later adding a member (which would newly prevent the
optimization) results in the generated code for its member functions
changing, and possibly the inner class type's size/layout as well. I'd
be concerned that that makes it too easy to break compatibility when
changing code.
The case with an inline inner class is different, since the request for
the optimization is explict (via making the inner class inline).
Changing an inline class to non-inline or vice versa is a change that
much more obviously may affect BC/ABI.
> By contrast, the question of pointer vs. offset is purely implementation.
> So long as the requested behavior (inner classes with state can maintain
> trivial copyability) has *some* implementation, and this implementation is
> not burdensome on compilers or at runtime, then the question of the
> implementation is more or less moot.
Sure. I don't think a full-pointer implementation is *possible* with the
relocatable constraint, but I agree that the standardese can make that
an implicit (due to what is possible within the behavioral constraints)
rather than explicit requirement.
> For example, I would say that inner classes should be as usable as regular
> classes except where that use would break the concept.
Sure (and for inline classes also).
> Allocating an inner class on the stack or heap directly makes no
> sense, nor does trying to use an inner class from type A inside a
> class of type B
Right.
> (though that brings up a new question of behavior: if B were derived
> from A, could B create new instances of A's inner classes?).
This wouldn't work for *virtual* bases. For non-virtual bases, I don't
see a problem, or a reason to forbid it.
Loosely related: B should be able to define an inner class type that
subclasses an inner class type of A.
> Placement new also does not make sense on inner classes (though
> obviously you can use it on non-inner classes that contain inner
> class members).
Agreed.
> Inner classes ought to be able to have constructors, and the owning class
> should be able to pass parameters to them. The fact that the compiler will
> generate some extra code for those constructors is more or less irrelevant.
Agreed.
> Inner classes should be able to have copy/move constructors if the user so
> desires.
Hmm... okay. Agreed.
> Similarly, NSDM's of inner class types should be as functional as any
> variable of any class type, except where that functionality does not make
> sense. So getting pointers/references should work, and they should be just
> as functional as they would be for a regular class type.
Agreed. Basically, an inner class is a regular class with
compiler-generated magic to tie it to its parent. (Basically, nothing we
can't do today, just more convenient.)
> However, it makes no sense for a user to explicitly invoke the
> copy/move action of an inner class member, so that can be forbidden.
> You can copy/move the owning class, but not an inner class member
> directly.
I want to say "huh?", but I think I am not following you.
Are you talking about forbidding a copy/move action of an inner class
type that occurs outside the context of the containing class's copy/move
ctor? (That is, no passing inner class types by value?) If yes, then agreed.
> On Thursday, September 3, 2015 at 11:18:58 AM UTC-4, Matthew Woehlke wrote:
>> I can't figure out any way in which an array of empty entities
>> would be useful... they are all identical, so what is the point of
>> having more than one of them?
>
> You might have some template code where the implementation creates a struct
> that contains an array of some type.
>
> But this is also a good reason why it's OK to forbid it: because it's
> ultimately not very useful even if it were allowed ;)
Right :-). Also, this is very much a corner case; you have some
template, and a type that is an inner class that is *also* an inline
class. In fact, I'm not even sure you can arrange such an instance
except that it would *always* be broken.
> That being said, we would also need some `std::is_inner_type` to detect
> when something is an inner class or not.
True, and good point. I had that for inline classes; it makes sense for
splitting the concepts to have a trait for both.
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Thu, 3 Sep 2015 19:44:19 -0700 (PDT)
Raw View
------=_Part_277_1872530300.1441334659910
Content-Type: multipart/alternative;
boundary="----=_Part_278_112463843.1441334659910"
------=_Part_278_112463843.1441334659910
Content-Type: text/plain; charset=UTF-8
On Thursday, September 3, 2015 at 5:11:00 PM UTC-4, Matthew Woehlke wrote:
>
> On 2015-09-03 15:21, Nicol Bolas wrote:
> > There may be a bit of a misunderstanding on what I was trying to say.
>
> Maybe, maybe not... my point was merely that we need to discuss the
> matter (whether starting from behavior or from implementation). I wasn't
> getting a clear sense of your feelings in that respect from the previous
> post. Whereas...
>
> > For example, Miro's suggestion is not merely implementation; it is
> > *behavior* (as well). It says that inner classes are all templates, with
> an
> > implicit CRTP use. So each inner class member is a separate type. Such
> > behavior is apparent and it has consequences for how the feature could
> be
> > used.
> >
> > For example, if you want a function that takes a reference to an inner
> > class member, you can't do that under his proposal without making this a
> > template function. Whereas if it were just a concrete class, you could.
> Is
> > this behavior we really want?
> >
> > I don't think so.
>
> ...this is much clearer; thanks :-).
>
> I also tend to agree.
>
> I think we agree that, behaviorally, an inner class should be trivially
> copyable, and that all members of a particular inner class type should
> actually be of that type, i.e. no hidden/implicit/magic templates.
>
>
> I think I would also refrain from encouraging any implicit optimization
> in the case of non-inline inner classes. The trouble here is that doing
> so means that later adding a member (which would newly prevent the
> optimization) results in the generated code for its member functions
> changing, and possibly the inner class type's size/layout as well. I'd
> be concerned that that makes it too easy to break compatibility when
> changing code.
>
If you add a new (non-empty) member to a class, you've already made the
choice to break compatibility. Not unless you remove some other member to
compensate. Which obviously you can't do if the class had no members to
begin with.
I guess the biggest concern is that, with empty inner classes, the
containing class remains standard layout, which is obviously lost if you
make the inner class non-empty. It's unfortunate, but I don't think there's
really anything to be done for it.
Generally speaking, I think the use cases for empty inner classes and
non-empty ones are sufficiently different that the choice will typically be
made when the class's interface is being designed.
> (though that brings up a new question of behavior: if B were derived
> > from A, could B create new instances of A's inner classes?).
>
> This wouldn't work for *virtual* bases. For non-virtual bases, I don't
> see a problem, or a reason to forbid it.
>
I think it could work for both cases. Consider the case for non-virtual
inheritance.
Remember that the standard does not require compilers to put members of a
derived class after the members of base classes in memory. So when you
access a regular member of A through an object of type B, the compiler is
allowed to do some pointer fixup to convert the `this` pointer into a
pointer to A.
However, the inner class member is actually a member of B, not A. So what
does the compiler do?
If the inner class is not empty, then it will have a static offset from
itself (which resides in B) to the owning object (A). This will always
work, since the distance from the object to A will be fixed at compile
time. The offset may be negative on some compilers, but the mechanism works
nevertheless.
For empty inner classes, something different must happen. Since it is an
inner class of A, the compiler will have to convert the pointer to B into a
pointer to A, then use that as the `this` pointer. And that should be
something the compiler can easily do, since it does so already when
accessing members of A through an object of type B. It's just a
static_cast<A*>.
But if the compiler can do both of those... what prevents the compiler from
using these same tools in the case of virtual inheritance? Now, I admit
that I have little experience with how virtual inheritance gets
implemented. So I may be missing something critical.
I think the derived class is still laid out contiguously even in the case
of virtual inheritance. Therefore, the offset from a particular member to
the pointer to the virtual base is a static offset. And it's an offset that
(I think) does not change depending on other derived classes and so forth.
If that's true, the offset can be baked into the object instance.
Egh, the more I think about it, the less sure I am that a static offset
will work. Or at least, not a offset defined solely by the definition of
class B. Maybe someone who actually knows about this stuff can comment.
For empty inner classes, it simply does whatever a static_cast<Base*> would
do before calling the member. It may require some added pointer work,
fetching memory locations and such. But it ought to still work.
Then again, it's virtual inheritance; if we have to lose it, no big loss ;)
> However, it makes no sense for a user to explicitly invoke the
> > copy/move action of an inner class member, so that can be forbidden.
> > You can copy/move the owning class, but not an inner class member
> > directly.
>
> I want to say "huh?", but I think I am not following you.
>
> Are you talking about forbidding a copy/move action of an inner class
> type that occurs outside the context of the containing class's copy/move
> ctor? (That is, no passing inner class types by value?) If yes, then
> agreed.
>
I'm talking about this:
class Outer
{
<<inducer>> class Inner {...};
Inner a, b;
};
Outer out;
out.b = out.a; //Explicitly invoke copy assignment.
out.b = std::move(out.a); //Explicitly invoke move assignment.
Things like that should not be allowed. Similarly, you shouldn't be able to
copy/move construct them (which could only happen within a constructor's
initialization list or an NSDM initializer).
And no fair calling destructors explicitly either ;)
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_278_112463843.1441334659910
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Thursday, September 3, 2015 at 5:11:00 PM UTC-4, Matthew Woehlke wrote:<=
blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bord=
er-left: 1px #ccc solid;padding-left: 1ex;">On 2015-09-03 15:21, Nicol Bola=
s wrote:
<br>> There may be a bit of a misunderstanding on what I was trying to s=
ay.
<br>
<br>Maybe, maybe not... my point was merely that we need to discuss the
<br>matter (whether starting from behavior or from implementation). I wasn&=
#39;t
<br>getting a clear sense of your feelings in that respect from the previou=
s
<br>post. Whereas...
<br>
<br>> For example, Miro's suggestion is not merely implementation; i=
t is=20
<br>> *behavior* (as well). It says that inner classes are all templates=
, with an=20
<br>> implicit CRTP use. So each inner class member is a separate type. =
Such=20
<br>> behavior is apparent and it has consequences for how the feature c=
ould be=20
<br>> used.
<br>>=20
<br>> For example, if you want a function that takes a reference to an i=
nner=20
<br>> class member, you can't do that under his proposal without mak=
ing this a=20
<br>> template function. Whereas if it were just a concrete class, you c=
ould. Is=20
<br>> this behavior we really want?
<br>>=20
<br>> I don't think so.
<br>
<br>...this is much clearer; thanks :-).
<br>
<br>I also tend to agree.
<br>
<br>I think we agree that, behaviorally, an inner class should be trivially
<br>copyable, and that all members of a particular inner class type should
<br>actually be of that type, i.e. no hidden/implicit/magic templates.
<br>
<br>
<br>I think I would also refrain from encouraging any implicit optimization
<br>in the case of non-inline inner classes. The trouble here is that doing
<br>so means that later adding a member (which would newly prevent the
<br>optimization) results in the generated code for its member functions
<br>changing, and possibly the inner class type's size/layout as well. =
I'd
<br>be concerned that that makes it too easy to break compatibility when
<br>changing code.<br></blockquote><div><br>If you add a new (non-empty) me=
mber to a class, you've already made the choice to break compatibility.=
Not unless you remove some other member to compensate. Which obviously you=
can't do if the class had no members to begin with.<br><br>I guess the=
biggest concern is that, with empty inner classes, the containing class re=
mains standard layout, which is obviously lost if you make the inner class =
non-empty. It's unfortunate, but I don't think there's really a=
nything to be done for it.<br><br>Generally speaking, I think the use cases=
for empty inner classes and non-empty ones are sufficiently different that=
the choice will typically be made when the class's interface is being =
designed.<br><br></div><blockquote class=3D"gmail_quote" style=3D"margin: 0=
;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
> (though that brings up a new question of behavior: if B were derived
<br>> from A, could B create new instances of A's inner classes?).
<br>
<br>This wouldn't work for *virtual* bases. For non-virtual bases, I do=
n't
<br>see a problem, or a reason to forbid it.<br></blockquote><div><br>I thi=
nk it could work for both cases. Consider the case for non-virtual inherita=
nce.<br><br>Remember that the standard does not require compilers to put me=
mbers of a derived class after the members of base classes in memory. So wh=
en you access a regular member of A through an object of type B, the compil=
er is allowed to do some pointer fixup to convert the `this` pointer into a=
pointer to A.<br><br>However, the inner class member is actually a member =
of B, not A. So what does the compiler do?<br><br>If the inner class is not=
empty, then it will have a static offset from itself (which resides in B) =
to the owning object (A). This will always work, since the distance from th=
e object to A will be fixed at compile time. The offset may be negative on =
some compilers, but the mechanism works nevertheless.<br><br>For empty inne=
r classes, something different must happen. Since it is an inner class of A=
, the compiler will have to convert the pointer to B into a pointer to A, t=
hen use that as the `this` pointer. And that should be something the compil=
er can easily do, since it does so already when accessing members of A thro=
ugh an object of type B. It's just a static_cast<A*>.<br><br>But =
if the compiler can do both of those... what prevents the compiler from usi=
ng these same tools in the case of virtual inheritance? Now, I admit that I=
have little experience with how virtual inheritance gets implemented. So I=
may be missing something critical.<br><br>I think the derived class is sti=
ll laid out contiguously even in the case of virtual inheritance. Therefore=
, the offset from a particular member to the pointer to the virtual base is=
a static offset. And it's an offset that (I think) does not change dep=
ending on other derived classes and so forth. If that's true, the offse=
t can be baked into the object instance.<br><br>Egh, the more I think about=
it, the less sure I am that a static offset will work. Or at least, not a =
offset defined solely by the definition of class B. Maybe someone who actua=
lly knows about this stuff can comment.<br><br>For empty inner classes, it =
simply does whatever a static_cast<Base*> would do before calling the=
member. It may require some added pointer work, fetching memory locations =
and such. But it ought to still work.<br><br> Then again, it's virtual =
inheritance; if we have to lose it, no big loss ;)<br><br></div><div></div>=
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;">
> However, it makes no sense for a user to explicitly invoke the
<br>> copy/move action of an inner class member, so that can be forbidde=
n.
<br>> You can copy/move the owning class, but not an inner class member
<br>> directly.
<br>
<br>I want to say "huh?", but I think I am not following you.
<br>
<br>Are you talking about forbidding a copy/move action of an inner class
<br>type that occurs outside the context of the containing class's copy=
/move
<br>ctor? (That is, no passing inner class types by value?) If yes, then ag=
reed.<br></blockquote><div><br>I'm talking about this:<br><br><div clas=
s=3D"prettyprint" style=3D"background-color: rgb(250, 250, 250); border-col=
or: rgb(187, 187, 187); border-style: solid; border-width: 1px; word-wrap: =
break-word;"><code class=3D"prettyprint"><div class=3D"subprettyprint"><spa=
n style=3D"color: #008;" class=3D"styled-by-prettify">class</span><span sty=
le=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D"col=
or: #606;" class=3D"styled-by-prettify">Outer</span><span style=3D"color: #=
000;" class=3D"styled-by-prettify"><br></span><span style=3D"color: #660;" =
class=3D"styled-by-prettify">{</span><span style=3D"color: #000;" class=3D"=
styled-by-prettify"><br>=C2=A0 </span><span style=3D"color: #660;" class=3D=
"styled-by-prettify"><<</span><span style=3D"color: #000;" class=3D"s=
tyled-by-prettify">inducer</span><span style=3D"color: #660;" class=3D"styl=
ed-by-prettify">>></span><span style=3D"color: #000;" class=3D"styled=
-by-prettify"> </span><span style=3D"color: #008;" class=3D"styled-by-prett=
ify">class</span><span style=3D"color: #000;" class=3D"styled-by-prettify">=
</span><span style=3D"color: #606;" class=3D"styled-by-prettify">Inner</sp=
an><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span =
style=3D"color: #660;" class=3D"styled-by-prettify">{...};</span><span styl=
e=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 </span><span sty=
le=3D"color: #606;" class=3D"styled-by-prettify">Inner</span><span style=3D=
"color: #000;" class=3D"styled-by-prettify"> a</span><span style=3D"color: =
#660;" class=3D"styled-by-prettify">,</span><span style=3D"color: #000;" cl=
ass=3D"styled-by-prettify"> b</span><span style=3D"color: #660;" class=3D"s=
tyled-by-prettify">;</span><span style=3D"color: #000;" class=3D"styled-by-=
prettify"><br></span><span style=3D"color: #660;" class=3D"styled-by-pretti=
fy">};</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br>=
<br></span><span style=3D"color: #606;" class=3D"styled-by-prettify">Outer<=
/span><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><sp=
an style=3D"color: #008;" class=3D"styled-by-prettify">out</span><span styl=
e=3D"color: #660;" class=3D"styled-by-prettify">;</span><span style=3D"colo=
r: #000;" class=3D"styled-by-prettify"><br></span><span style=3D"color: #00=
8;" class=3D"styled-by-prettify">out</span><span style=3D"color: #660;" cla=
ss=3D"styled-by-prettify">.</span><span style=3D"color: #000;" class=3D"sty=
led-by-prettify">b </span><span style=3D"color: #660;" class=3D"styled-by-p=
rettify">=3D</span><span style=3D"color: #000;" class=3D"styled-by-prettify=
"> </span><span style=3D"color: #008;" class=3D"styled-by-prettify">out</sp=
an><span style=3D"color: #660;" class=3D"styled-by-prettify">.</span><span =
style=3D"color: #000;" class=3D"styled-by-prettify">a</span><span style=3D"=
color: #660;" class=3D"styled-by-prettify">;</span><span style=3D"color: #0=
00;" class=3D"styled-by-prettify"> </span><span style=3D"color: #800;" clas=
s=3D"styled-by-prettify">//Explicitly invoke copy assignment.</span><span s=
tyle=3D"color: #000;" class=3D"styled-by-prettify"><br></span><span style=
=3D"color: #008;" class=3D"styled-by-prettify">out</span><span style=3D"col=
or: #660;" class=3D"styled-by-prettify">.</span><span style=3D"color: #000;=
" class=3D"styled-by-prettify">b </span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">=3D</span><span style=3D"color: #000;" class=3D"sty=
led-by-prettify"> std</span><span style=3D"color: #660;" class=3D"styled-by=
-prettify">::</span><span style=3D"color: #000;" class=3D"styled-by-prettif=
y">move</span><span style=3D"color: #660;" class=3D"styled-by-prettify">(</=
span><span style=3D"color: #008;" class=3D"styled-by-prettify">out</span><s=
pan style=3D"color: #660;" class=3D"styled-by-prettify">.</span><span style=
=3D"color: #000;" class=3D"styled-by-prettify">a</span><span style=3D"color=
: #660;" class=3D"styled-by-prettify">);</span><span style=3D"color: #000;"=
class=3D"styled-by-prettify"> </span><span style=3D"color: #800;" class=3D=
"styled-by-prettify">//Explicitly invoke move assignment.</span></div></cod=
e></div><br>Things like that should not be allowed. Similarly, you shouldn&=
#39;t be able to copy/move construct them (which could only happen within a=
constructor's initialization list or an NSDM initializer).<br><br>And =
no fair calling destructors explicitly either ;)<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_278_112463843.1441334659910--
------=_Part_277_1872530300.1441334659910--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Fri, 04 Sep 2015 10:08:29 -0400
Raw View
On 2015-09-03 22:44, Nicol Bolas wrote:
> On Thursday, September 3, 2015 at 5:11:00 PM UTC-4, Matthew Woehlke wrote:
>> I think I would also refrain from encouraging any implicit optimization
>> in the case of non-inline inner classes. The trouble here is that doing
>> so means that later adding a member (which would newly prevent the
>> optimization) results in the generated code for its member functions
>> changing, and possibly the inner class type's size/layout as well. I'd
>> be concerned that that makes it too easy to break compatibility when
>> changing code.
>
> If you add a new (non-empty) member to a class, you've already made the
> choice to break compatibility. Not unless you remove some other member to
> compensate. Which obviously you can't do if the class had no members to
> begin with.
I'm thinking more of the case of a unique inner class member, for which
the compiler could optimize away the dynamic offset into a constant
offset. This would result in a size change of the *inner* class, which
may be unexpected; not just the expected change to the outer class. But
now that you mention it, I'm having a harder time convincing myself
that's a genuine issue.
Anyway, I'm not saying that I would *forbid* that, just not say anything
to *encourage* vendors to implement such an optimization. If vendors
wish to do so anyway, let the consequences be on their own heads :-).
> I guess the biggest concern is that, with empty inner classes, the
> containing class remains standard layout, which is obviously lost if you
> make the inner class non-empty. It's unfortunate, but I don't think there's
> really anything to be done for it.
Again, I would not mandate that a non-inline empty inner class has zero
size. Rather the opposite, if anything; I would be inclined that it has
a minimum size == sizeof(ptrdiff_t).
On a related note, is the size of a non-inline inner class equal to the
size of its members (plus usual padding) plus an additional ptrdiff_t?
(That is, *not* equal to the outer class size?)
> Generally speaking, I think the use cases for empty inner classes and
> non-empty ones are sufficiently different that the choice will typically be
> made when the class's interface is being designed.
Right. That is, what I said above; non-inline empty inner classes should
not specify any different behavior from non-empty inner classes. If you
change an inner class from inline to non-inline (or vice versa), that's
something that much more obviously will have ABI implications.
>> (though that brings up a new question of behavior: if B were derived
>>> from A, could B create new instances of A's inner classes?).
>>
>> This wouldn't work for *virtual* bases. For non-virtual bases, I don't
>> see a problem, or a reason to forbid it.
>
> I think it could work for both cases. Consider the case for
> non-virtual inheritance. [...] the inner class member is actually a
> member of B, not A. So what does the compiler do?
>
> If the inner class is not empty, then it will have a static offset from
> itself (which resides in B) to the owning object (A). This will always
> work, since the distance from the object to A will be fixed at compile
> time. The offset may be negative on some compilers, but the mechanism works
> nevertheless.
Right; exactly as I would expect.
> For empty inner classes, something different must happen. Since it is an
> inner class of A, the compiler will have to convert the pointer to B into a
> pointer to A, then use that as the `this` pointer. And that should be
> something the compiler can easily do, since it does so already when
> accessing members of A through an object of type B. It's just a
> static_cast<A*>.
Right.
> But if the compiler can do both of those... what prevents the compiler from
> using these same tools in the case of virtual inheritance? Now, I admit
> that I have little experience with how virtual inheritance gets
> implemented. So I may be missing something critical.
>
> I think the derived class is still laid out contiguously even in the case
> of virtual inheritance.
Um... for the *final* class, yes. Hmm...
Maybe this does work, but it's just complicated. Assume that the storage
is contiguous (i.e. it is still rational to use offsets). The issue
arises in that, let's say we have class D derived from B1 and B2, both
having virtual base A, and B1 having an inner class member of type A::I.
Supposedly then, D knows the static offset from B1's inner class to A.
However, the offset needs to be initialized by B1, which does *not* know
where A lives relative to it. Presumably upon construction, however, B1
already knows where it's A lives. So it could compute the offset at that
point. The difference is that the offset must be computed by B1's ctor
based on the actual offset to A, rather than being constant.
I think that can work... it just means that the offset isn't a
compile-time constant, because it depends on what derived class is
actually instantiated.
Okay, I think we agree to not forbid it a priori and wait to see if
implementation is possible.
> For empty inner classes, it simply does whatever a static_cast<Base*> would
> do before calling the member. It may require some added pointer work,
> fetching memory locations and such. But it ought to still work.
Right; no problem for this case.
>> On 2015-09-03 15:21, Nicol Bolas wrote:
>>> However, it makes no sense for a user to explicitly invoke the
>>> copy/move action of an inner class member, so that can be forbidden.
>>> You can copy/move the owning class, but not an inner class member
>>> directly.
>>
>> Are you talking about forbidding a copy/move action of an inner class
>> type that occurs outside the context of the containing class's copy/move
>> ctor? (That is, no passing inner class types by value?) If yes, then
>> agreed.
>
> I'm talking about this:
>
> class Outer
> {
> <<inducer>> class Inner {...};
> Inner a, b;
> };
>
> Outer out;
> out.b = out.a; //Explicitly invoke copy assignment.
> out.b = std::move(out.a); //Explicitly invoke move assignment.
>
> Things like that should not be allowed.
Why not? An *assignment operator* doesn't have anything to do with
storage; the target object already exists, and the source object doesn't
cease to exist. Even a move ctor is required to leave the source object
in a *valid* state.
> Similarly, you shouldn't be able to copy/move construct them (which
> could only happen within a constructor's initialization list or an
> NSDM initializer).
You definitely *should* be able to copy/move construct them in the
context of the outer class's ctors. In any *other* context, however,
that should be illegal. IOW:
void foo(Outer::Inner&); // okay
void foo(Outer::Inner); // error
The problem of course is that this would require instantiating an inner
class outside of its outer class.
Passing an inner class as an rvalue-reference is a little bit trickier,
since this should ideally be allowed:
Outer outer;
bar(std::move(outer).a);
(May require lifetime extension of 'outer'...)
I'm starting to think that the only thing that should be explicitly
specified is to forbid instantiation outside of the outer class, and let
any restrictions on copy/move flow out of that.
> And no fair calling destructors explicitly either ;)
I guess this would be the flip side of placement new... so, yeah, makes
sense.
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Fri, 4 Sep 2015 09:51:09 -0700 (PDT)
Raw View
------=_Part_1040_93120315.1441385469779
Content-Type: multipart/alternative;
boundary="----=_Part_1041_976459708.1441385469779"
------=_Part_1041_976459708.1441385469779
Content-Type: text/plain; charset=UTF-8
On Friday, September 4, 2015 at 10:08:48 AM UTC-4, Matthew Woehlke wrote:
>
> On 2015-09-03 22:44, Nicol Bolas wrote:
> > On Thursday, September 3, 2015 at 5:11:00 PM UTC-4, Matthew Woehlke
> wrote:
> >> On 2015-09-03 15:21, Nicol Bolas wrote:
> >>> However, it makes no sense for a user to explicitly invoke the
> >>> copy/move action of an inner class member, so that can be forbidden.
> >>> You can copy/move the owning class, but not an inner class member
> >>> directly.
> >>
> >> Are you talking about forbidding a copy/move action of an inner class
> >> type that occurs outside the context of the containing class's
> copy/move
> >> ctor? (That is, no passing inner class types by value?) If yes, then
> >> agreed.
> >
> > I'm talking about this:
> >
> > class Outer
> > {
> > <<inducer>> class Inner {...};
> > Inner a, b;
> > };
> >
> > Outer out;
> > out.b = out.a; //Explicitly invoke copy assignment.
> > out.b = std::move(out.a); //Explicitly invoke move assignment.
> >
> > Things like that should not be allowed.
>
> Why not? An *assignment operator* doesn't have anything to do with
> storage; the target object already exists, and the source object doesn't
> cease to exist. Even a move ctor is required to leave the source object
> in a *valid* state.
>
Here's the problem. Remember what the actual inner class looks like:
<<inducer>> class Inner
{
....
int32_t __offset;
};
Regardless of the specific information, each class member has its own
distinct offset, which goes from that class member's `this` to the owning
class's `this`.
So `out.a.__offset` and `out.b.__offset` are different values.
However, if Inner is actually trivially copyable, then that means you have
to be able to copy it via memcpy. The trivial copy constructor/assignment
can also be a memcpy.
So... what happens if you do `memcpy(&out.b, &out.a,
sizeof(Outer::Inner))`? That's right, you copy `out.a.__offset` into
`out.b.__offset`. So now `out.b` has the wrong offset.
That's bad.
I don't think there's a way to get around that without expressly forbidding
the copying/moving of inner class members. It's a weird thing; the actual
inner class is non-copyable at all, but the class that contains it as a
member is copyable (and trivially so where applicable).
Ugh, that's going to be a painful thing to standardize.
>
> > Similarly, you shouldn't be able to copy/move construct them (which
> > could only happen within a constructor's initialization list or an
> > NSDM initializer).
>
> You definitely *should* be able to copy/move construct them in the
> context of the outer class's ctors.
I understand the need for it, but it's coming dangerously close to saying
that stateful inner classes interfere with trivial copyability.
A trivial copy constructor should be equivalent to a memcpy. But for
stateful inner classes, one of their members is a value that is intrinsic
to that specific member. So memcpying it is non-functional. Therefore, the
copy constructor cannot be trivial.
The only way I can see to avoid that is to disallow explicit copy
constructor invocation. Then, the compiler can just do a bit of magic for
implicit copy constructor use.
However, I realize that if you disallow this, then the owning class can't
have a explicit copy constructor that copies stateful inner class members.
So the owning class is forced to rely on implicit copy/move constructors
only. That's also bad.
I don't like where this is going...
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_1041_976459708.1441385469779
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Friday, September 4, 2015 at 10:08:48 AM UTC-4, Matthew Woehlke wrote:<b=
lockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;borde=
r-left: 1px #ccc solid;padding-left: 1ex;">On 2015-09-03 22:44, Nicol Bolas=
wrote:
<br>> On Thursday, September 3, 2015 at 5:11:00 PM UTC-4, Matthew Woehlk=
e wrote:
<br>>> On 2015-09-03 15:21, Nicol Bolas wrote:=20
<br>>>> However, it makes no sense for a user to explicitly invoke=
the=20
<br>>>> copy/move action of an inner class member, so that can be =
forbidden.=20
<br>>>> You can copy/move the owning class, but not an inner class=
member=20
<br>>>> directly.
<br>>>
<br>>> Are you talking about forbidding a copy/move action of an inne=
r class=20
<br>>> type that occurs outside the context of the containing class&#=
39;s copy/move=20
<br>>> ctor? (That is, no passing inner class types by value?) If yes=
, then=20
<br>>> agreed.
<br>>=20
<br>> I'm talking about this:
<br>>=20
<br>> class Outer
<br>> {
<br>> =C2=A0 <<inducer>> class Inner {...};
<br>> =C2=A0 Inner a, b;
<br>> };
<br>>=20
<br>> Outer out;
<br>> out.b =3D out.a; //Explicitly invoke copy assignment.
<br>> out.b =3D std::move(out.a); //Explicitly invoke move assignment.
<br>>=20
<br>> Things like that should not be allowed.
<br>
<br>Why not? An *assignment operator* doesn't have anything to do with
<br>storage; the target object already exists, and the source object doesn&=
#39;t
<br>cease to exist. Even a move ctor is required to leave the source object
<br>in a *valid* state.<br></blockquote><div><br>Here's the problem. Re=
member what the actual inner class looks like:<br><br><div class=3D"prettyp=
rint" style=3D"background-color: rgb(250, 250, 250); border-color: rgb(187,=
187, 187); border-style: solid; border-width: 1px; word-wrap: break-word;"=
><code class=3D"prettyprint"><div class=3D"subprettyprint"><span style=3D"c=
olor: #000;" class=3D"styled-by-prettify"><</span><span style=3D"color: =
#008;" class=3D"styled-by-prettify"><inducer></span><span style=3D"co=
lor: #000;" class=3D"styled-by-prettify">> class Inner<br>{<br>...<br>in=
t32_t __offset;<br>};</span></div></code></div><br>Regardless of the specif=
ic information, each class member has its own distinct offset, which goes f=
rom that class member's `this` to the owning class's `this`.<br><br=
>So `out.a.__offset` and `out.b.__offset` are different values.<br><br>Howe=
ver, if Inner is actually trivially copyable, then that means you have to b=
e able to copy it via memcpy. The trivial copy constructor/assignment can a=
lso be a memcpy.<br><br>So... what happens if you do `memcpy(&out.b, &a=
mp;out.a, sizeof(Outer::Inner))`? That's right, you copy `out.a.__offse=
t` into `out.b.__offset`. So now `out.b` has the wrong offset.<br><br>That&=
#39;s bad.<br><br>I don't think there's a way to get around that wi=
thout expressly forbidding the copying/moving of inner class members. It=
9;s a weird thing; the actual inner class is non-copyable at all, but the c=
lass that contains it as a member is copyable (and trivially so where appli=
cable).<br><br>Ugh, that's going to be a painful thing to standardize.<=
br>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-=
left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
<br>> Similarly, you shouldn't be able to copy/move construct them (=
which
<br>> could only happen within a constructor's initialization list o=
r an
<br>> NSDM initializer).
<br>
<br>You definitely *should* be able to copy/move construct them in the
<br>context of the outer class's ctors.</blockquote><div><br>I understa=
nd the need for it, but it's coming dangerously close to saying that st=
ateful inner classes interfere with trivial copyability.<br><br>A trivial c=
opy constructor should be equivalent to a memcpy. But for stateful inner cl=
asses, one of their members is a value that is intrinsic to that specific m=
ember. So memcpying it is non-functional. Therefore, the copy constructor c=
annot be trivial.<br><br>The only way I can see to avoid that is to disallo=
w explicit copy constructor invocation. Then, the compiler can just do a bi=
t of magic for implicit copy constructor use.<br><br>However, I realize tha=
t if you disallow this, then the owning class can't have a explicit cop=
y constructor that copies stateful inner class members. So the owning class=
is forced to rely on implicit copy/move constructors only. That's also=
bad.<br><br>I don't like where this is going...<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_1041_976459708.1441385469779--
------=_Part_1040_93120315.1441385469779--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Fri, 04 Sep 2015 13:51:33 -0400
Raw View
On 2015-09-04 12:51, Nicol Bolas wrote:
> On Friday, September 4, 2015 at 10:08:48 AM UTC-4, Matthew Woehlke wrote:
>> On 2015-09-03 22:44, Nicol Bolas wrote:
>>> class Outer
>>> {
>>> <<inducer>> class Inner {...};
>>> Inner a, b;
>>> };
>>>
>>> Outer out;
>>> out.b = out.a; //Explicitly invoke copy assignment.
>>> out.b = std::move(out.a); //Explicitly invoke move assignment.
>>>
>>> Things like that should not be allowed.
>>
>> Why not? An *assignment operator* doesn't have anything to do with
>> storage; the target object already exists, and the source object doesn't
>> cease to exist. Even a move ctor is required to leave the source object
>> in a *valid* state.
>
> Here's the problem. Remember what the actual inner class looks like:
>
> <<inducer>> class Inner
> {
> ...
> int32_t __offset;
> };
>
> Regardless of the specific information, each class member has its own
> distinct offset, which goes from that class member's `this` to the owning
> class's `this`.
>
> So `out.a.__offset` and `out.b.__offset` are different values.
>
> However, if Inner is actually trivially copyable, then that means you have
> to be able to copy it via memcpy. The trivial copy constructor/assignment
> can also be a memcpy.
I think that the offset should/would not be accessible to the user, but
always managed by the containing class.
Rather than thinking of the offset as a data member, it might be helpful
to think of it as something like the extra information that a heap
allocator may place before/after a heap allocation (e.g. the size of the
chunk). This data is (considered as) not user accessible and is a
function of the allocation, not the data contained in the chunk.
Similarly, an inner class's offset is a function of its instantiation by
a containing class is is not "part" of the inner class for the purpose
of copying the inner class (although it is counted in sizeof()).
This does mean that, conceptually, a default copy ctor for an outer
class looks like:
- call base class ctor
- set offsets in inner class members
- call copy ctors for all class members (inner classes or otherwise)
Of course, the compiler can know that the offsets of the inner class
members of the source and target are the same, and therefore if the type
is otherwise relocatably (i.e. copy by memcpy/memmove), then the offsets
are also, and so the whole thing can be one big memcpy/memmove.
(Or are they? I'm not sure in the case of virtual inheritance if the
source and target aren't actually the same type, but then, in such case
you're probably having to fix up the base pointers anyway, so already
you lost relocatability. IOW I still don't see a problem. In fact, I
wonder if virtual inheritance hasn't already had to address these issues...)
> So... what happens if you do `memcpy(&out.b, &out.a,
> sizeof(Outer::Inner))`? That's right, you copy `out.a.__offset` into
> `out.b.__offset`. So now `out.b` has the wrong offset.
>
> That's bad.
So... don't do that :-). That's clearly UB, same as if you wrote
directly into a class's vptr or other such nonsense.
The compiler should have no problem making the necessary adjustments so
that a relocating copy ctor for an inner class does not clobber the offset.
>> You definitely *should* be able to copy/move construct them in the
>> context of the outer class's ctors.
Specifically, I mean this had better work, or we have a real mess:
Outer::Outer(Outer const& other) : inner(other.inner)
{
...
}
Having a non-default copy ctor for the outer class require that the user
write out how to copy the inner class members would be... as you said:
> It's a weird thing; the actual inner class is non-copyable at all,
> but the class that contains it as a member is copyable (and trivially
> so where applicable).
....or in my own word, "atrocious" :-). And...
> Ugh, that's going to be a painful thing to standardize.
....I would go so far as to say that more strongly; not merely "painful",
but likely "guaranteed to kill the proposal".
So I think we agree to not go there. Fortunately, I don't think we need to.
> I understand the need for it, but it's coming dangerously close to saying
> that stateful inner classes interfere with trivial copyability.
>
> A trivial copy constructor should be equivalent to a memcpy.
See above. If we state that the outer class is responsible for
maintaining the offsets (which makes sense; the offset is an artifact of
the inner class's location in the outer class, *not* the inner class's
own data), and that the inner class's copying does not touch the offset,
then I think we are okay. (Sure, this means that the ownership model
is... unusual. But I don't see an *implementation* problem.)
It also occurs to me that since we aren't legislating the
implementation, this is all implementation details...
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Fri, 4 Sep 2015 11:38:51 -0700 (PDT)
Raw View
------=_Part_567_88926146.1441391931596
Content-Type: multipart/alternative;
boundary="----=_Part_568_1550366784.1441391931596"
------=_Part_568_1550366784.1441391931596
Content-Type: text/plain; charset=UTF-8
On Friday, September 4, 2015 at 1:51:56 PM UTC-4, Matthew Woehlke wrote:
>
> On 2015-09-04 12:51, Nicol Bolas wrote:
> > On Friday, September 4, 2015 at 10:08:48 AM UTC-4, Matthew Woehlke
> wrote:
> >> On 2015-09-03 22:44, Nicol Bolas wrote:
> >>> class Outer
> >>> {
> >>> <<inducer>> class Inner {...};
> >>> Inner a, b;
> >>> };
> >>>
> >>> Outer out;
> >>> out.b = out.a; //Explicitly invoke copy assignment.
> >>> out.b = std::move(out.a); //Explicitly invoke move assignment.
> >>>
> >>> Things like that should not be allowed.
> >>
> >> Why not? An *assignment operator* doesn't have anything to do with
> >> storage; the target object already exists, and the source object
> doesn't
> >> cease to exist. Even a move ctor is required to leave the source object
> >> in a *valid* state.
> >
> > Here's the problem. Remember what the actual inner class looks like:
> >
> > <<inducer>> class Inner
> > {
> > ...
> > int32_t __offset;
> > };
> >
> > Regardless of the specific information, each class member has its own
> > distinct offset, which goes from that class member's `this` to the
> owning
> > class's `this`.
> >
> > So `out.a.__offset` and `out.b.__offset` are different values.
> >
> > However, if Inner is actually trivially copyable, then that means you
> have
> > to be able to copy it via memcpy. The trivial copy
> constructor/assignment
> > can also be a memcpy.
>
> I think that the offset should/would not be accessible to the user, but
> always managed by the containing class.
>
That's why I used the double-underscore on the member name. It's there for
exposition, but is an implementation detail that's not accessible by the
user.
My point is that, user-accessible or not, it is part of `Inner`'s storage.
And there's no other way to implement it. After all, code may only have a
pointer/reference to the `Inner`. Therefore, *each* `Inner` instance must
have sufficient data to compute the location of its containing `Outer`
object from just its own `this` pointer.
So putting those offsets in the owning class somewhere won't work.
> > So... what happens if you do `memcpy(&out.b, &out.a,
> > sizeof(Outer::Inner))`? That's right, you copy `out.a.__offset` into
> > `out.b.__offset`. So now `out.b` has the wrong offset.
> >
> > That's bad.
>
> So... don't do that :-). That's clearly UB, same as if you wrote
> directly into a class's vptr or other such nonsense.
>
Ah, but there's the difference: classes with vptrs are not trivially
copyable. If you're saying that you can't memcpy it, then it's not
trivially copyable (ie: the entire point of the trivially copyable notion).
Which is something we don't want to do unless there's no other choice.
> The compiler should have no problem making the necessary adjustments so
> that a relocating copy ctor for an inner class does not clobber the offset.
>
Absolutely. But it would not be a *trivial* copy constructor
<http://en.cppreference.com/w/cpp/language/copy_constructor#Trivial_copy_constructor>
anymore.
> > I understand the need for it, but it's coming dangerously close to
> saying
> > that stateful inner classes interfere with trivial copyability.
> >
> > A trivial copy constructor should be equivalent to a memcpy.
>
> See above. If we state that the outer class is responsible for
> maintaining the offsets (which makes sense; the offset is an artifact of
> the inner class's location in the outer class, *not* the inner class's
> own data), and that the inner class's copying does not touch the offset,
> then I think we are okay. (Sure, this means that the ownership model
> is... unusual. But I don't see an *implementation* problem.)
>
> It also occurs to me that since we aren't legislating the
> implementation, this is all implementation details...
>
Trivially copyable is *behavior*; it's something you have to specify in the
standard. And if that behavior cannot be implemented, then that behavior
cannot be permitted by the standard.
So it's not an implementation detail. The need to implement it is getting
in the way of the desired behavior.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_568_1550366784.1441391931596
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Friday, September 4, 2015 at 1:51:56 PM UTC-4, Matthew Woehlke wrote:<bl=
ockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border=
-left: 1px #ccc solid;padding-left: 1ex;">On 2015-09-04 12:51, Nicol Bolas =
wrote:
<br>> On Friday, September 4, 2015 at 10:08:48 AM UTC-4, Matthew Woehlke=
wrote:
<br>>> On 2015-09-03 22:44, Nicol Bolas wrote:=20
<br>>>> class Outer=20
<br>>>> {=20
<br>>>> =C2=A0 <<inducer>> class Inner {...};=20
<br>>>> =C2=A0 Inner a, b;=20
<br>>>> };=20
<br>>>>
<br>>>> Outer out;=20
<br>>>> out.b =3D out.a; //Explicitly invoke copy assignment.=20
<br>>>> out.b =3D std::move(out.a); //Explicitly invoke move assig=
nment.=20
<br>>>>
<br>>>> Things like that should not be allowed.=20
<br>>>
<br>>> Why not? An *assignment operator* doesn't have anything to=
do with=20
<br>>> storage; the target object already exists, and the source obje=
ct doesn't=20
<br>>> cease to exist. Even a move ctor is required to leave the sour=
ce object=20
<br>>> in a *valid* state.
<br>>=20
<br>> Here's the problem. Remember what the actual inner class looks=
like:
<br>>=20
<br>> <<inducer>> class Inner
<br>> {
<br>> ...
<br>> int32_t __offset;
<br>> };
<br>>=20
<br>> Regardless of the specific information, each class member has its =
own=20
<br>> distinct offset, which goes from that class member's `this` to=
the owning=20
<br>> class's `this`.
<br>>=20
<br>> So `out.a.__offset` and `out.b.__offset` are different values.
<br>>=20
<br>> However, if Inner is actually trivially copyable, then that means =
you have=20
<br>> to be able to copy it via memcpy. The trivial copy constructor/ass=
ignment=20
<br>> can also be a memcpy.
<br>
<br>I think that the offset should/would not be accessible to the user, but
<br>always managed by the containing class.
<br></blockquote><div><br>That's why I used the double-underscore on th=
e member name. It's there for exposition, but is an implementation deta=
il that's not accessible by the user.<br><br>My point is that, user-acc=
essible or not, it is part of `Inner`'s storage. And there's no oth=
er way to implement it. After all, code may only have a pointer/reference t=
o the `Inner`. Therefore, <i>each</i> `Inner` instance must have sufficient=
data to compute the location of its containing `Outer` object from just it=
s own `this` pointer.<br><br>So putting those offsets in the owning class s=
omewhere won't work.<br>=C2=A0</div><blockquote class=3D"gmail_quote" s=
tyle=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-le=
ft: 1ex;">
> So... what happens if you do `memcpy(&out.b, &out.a,=20
<br>> sizeof(Outer::Inner))`? That's right, you copy `out.a.__offset=
` into=20
<br>> `out.b.__offset`. So now `out.b` has the wrong offset.
<br>>=20
<br>> That's bad.
<br>
<br>So... don't do that :-). That's clearly UB, same as if you wrot=
e
<br>directly into a class's vptr or other such nonsense.<br></blockquot=
e><div><br>Ah, but there's the difference: classes with vptrs are not t=
rivially copyable. If you're saying that you can't memcpy it, then =
it's not trivially copyable (ie: the entire point of the trivially copy=
able notion). Which is something we don't want to do unless there's=
no other choice.<br>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D=
"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex=
;">
The compiler should have no problem making the necessary adjustments so
<br>that a relocating copy ctor for an inner class does not clobber the off=
set.<br></blockquote><div><br>Absolutely. But it would not be a <a href=3D"=
http://en.cppreference.com/w/cpp/language/copy_constructor#Trivial_copy_con=
structor"><i>trivial</i> copy constructor </a>anymore.<br>=C2=A0</div><bloc=
kquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-l=
eft: 1px #ccc solid;padding-left: 1ex;">
> I understand the need for it, but it's coming dangerously close to=
saying=20
<br>> that stateful inner classes interfere with trivial copyability.
<br>>=20
<br>> A trivial copy constructor should be equivalent to a memcpy.
<br>
<br>See above. If we state that the outer class is responsible for
<br>maintaining the offsets (which makes sense; the offset is an artifact o=
f
<br>the inner class's location in the outer class, *not* the inner clas=
s's
<br>own data), and that the inner class's copying does not touch the of=
fset,
<br>then I think we are okay. (Sure, this means that the ownership model
<br>is... unusual. But I don't see an *implementation* problem.)
<br>
<br>It also occurs to me that since we aren't legislating the
<br>implementation, this is all implementation details...<br></blockquote><=
div><br>Trivially copyable is <i>behavior</i>; it's something you have =
to specify in the standard. And if that behavior cannot be implemented, the=
n that behavior cannot be permitted by the standard.<br><br>So it's not=
an implementation detail. The need to implement it is getting in the way o=
f the desired behavior.<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_568_1550366784.1441391931596--
------=_Part_567_88926146.1441391931596--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Fri, 4 Sep 2015 11:51:57 -0700 (PDT)
Raw View
------=_Part_957_657483540.1441392717567
Content-Type: multipart/alternative;
boundary="----=_Part_958_775666694.1441392717567"
------=_Part_958_775666694.1441392717567
Content-Type: text/plain; charset=UTF-8
On Friday, September 4, 2015 at 2:38:52 PM UTC-4, Nicol Bolas wrote:
>
> On Friday, September 4, 2015 at 1:51:56 PM UTC-4, Matthew Woehlke wrote:
>>
>> On 2015-09-04 12:51, Nicol Bolas wrote:
>> > On Friday, September 4, 2015 at 10:08:48 AM UTC-4, Matthew Woehlke
>> wrote:
>> >> On 2015-09-03 22:44, Nicol Bolas wrote:
>> >>> class Outer
>> >>> {
>> >>> <<inducer>> class Inner {...};
>> >>> Inner a, b;
>> >>> };
>> >>>
>> >>> Outer out;
>> >>> out.b = out.a; //Explicitly invoke copy assignment.
>> >>> out.b = std::move(out.a); //Explicitly invoke move assignment.
>> >>>
>> >>> Things like that should not be allowed.
>> >>
>> >> Why not? An *assignment operator* doesn't have anything to do with
>> >> storage; the target object already exists, and the source object
>> doesn't
>> >> cease to exist. Even a move ctor is required to leave the source
>> object
>> >> in a *valid* state.
>> >
>> > Here's the problem. Remember what the actual inner class looks like:
>> >
>> > <<inducer>> class Inner
>> > {
>> > ...
>> > int32_t __offset;
>> > };
>> >
>> > Regardless of the specific information, each class member has its own
>> > distinct offset, which goes from that class member's `this` to the
>> owning
>> > class's `this`.
>> >
>> > So `out.a.__offset` and `out.b.__offset` are different values.
>> >
>> > However, if Inner is actually trivially copyable, then that means you
>> have
>> > to be able to copy it via memcpy. The trivial copy
>> constructor/assignment
>> > can also be a memcpy.
>>
>> I think that the offset should/would not be accessible to the user, but
>> always managed by the containing class.
>>
>
> That's why I used the double-underscore on the member name. It's there for
> exposition, but is an implementation detail that's not accessible by the
> user.
>
> My point is that, user-accessible or not, it is part of `Inner`'s storage.
> And there's no other way to implement it. After all, code may only have a
> pointer/reference to the `Inner`. Therefore, *each* `Inner` instance must
> have sufficient data to compute the location of its containing `Outer`
> object from just its own `this` pointer.
>
> So putting those offsets in the owning class somewhere won't work.
>
Wait, I think I understand sort of what you were getting at. It still won't
work, but for a different reason.
You were basically saying that inner class implementations would assume
that the four bytes preceeding its `this` pointer would contain the offset.
And that it's up to how the individual owning class(es) lay those objects
out to set those values. So you can copy individual members just fine,
since the offset isn't strictly part of `this`. And an instance can simply
decrement `this` four bytes and get the offset, since instances of inner
classes can only be created within another class.
The problem then is with arrays of inner classes. Earlier, we forbade
arrays of empty classes because they don't make sense and would have to
take up space. But arrays of stateful inner classes couldn't work with this
either. The reason being that arrays have to be contiguous, that the offset
from one array entry to another must be `sizeof(T)`. So there's no room
available for an offset.
Not unless that offset is *part* of `sizeof(T)`...
So either way, the offset has to be a part of the type.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_958_775666694.1441392717567
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<br><br>On Friday, September 4, 2015 at 2:38:52 PM UTC-4, Nicol Bolas wrote=
:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bo=
rder-left: 1px #ccc solid;padding-left: 1ex;">On Friday, September 4, 2015 =
at 1:51:56 PM UTC-4, Matthew Woehlke wrote:<blockquote class=3D"gmail_quote=
" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-le=
ft:1ex">On 2015-09-04 12:51, Nicol Bolas wrote:
<br>> On Friday, September 4, 2015 at 10:08:48 AM UTC-4, Matthew Woehlke=
wrote:
<br>>> On 2015-09-03 22:44, Nicol Bolas wrote:=20
<br>>>> class Outer=20
<br>>>> {=20
<br>>>> =C2=A0 <<inducer>> class Inner {...};=20
<br>>>> =C2=A0 Inner a, b;=20
<br>>>> };=20
<br>>>>
<br>>>> Outer out;=20
<br>>>> out.b =3D out.a; //Explicitly invoke copy assignment.=20
<br>>>> out.b =3D std::move(out.a); //Explicitly invoke move assig=
nment.=20
<br>>>>
<br>>>> Things like that should not be allowed.=20
<br>>>
<br>>> Why not? An *assignment operator* doesn't have anything to=
do with=20
<br>>> storage; the target object already exists, and the source obje=
ct doesn't=20
<br>>> cease to exist. Even a move ctor is required to leave the sour=
ce object=20
<br>>> in a *valid* state.
<br>>=20
<br>> Here's the problem. Remember what the actual inner class looks=
like:
<br>>=20
<br>> <<inducer>> class Inner
<br>> {
<br>> ...
<br>> int32_t __offset;
<br>> };
<br>>=20
<br>> Regardless of the specific information, each class member has its =
own=20
<br>> distinct offset, which goes from that class member's `this` to=
the owning=20
<br>> class's `this`.
<br>>=20
<br>> So `out.a.__offset` and `out.b.__offset` are different values.
<br>>=20
<br>> However, if Inner is actually trivially copyable, then that means =
you have=20
<br>> to be able to copy it via memcpy. The trivial copy constructor/ass=
ignment=20
<br>> can also be a memcpy.
<br>
<br>I think that the offset should/would not be accessible to the user, but
<br>always managed by the containing class.
<br></blockquote><div><br>That's why I used the double-underscore on th=
e member name. It's there for exposition, but is an implementation deta=
il that's not accessible by the user.<br><br>My point is that, user-acc=
essible or not, it is part of `Inner`'s storage. And there's no oth=
er way to implement it. After all, code may only have a pointer/reference t=
o the `Inner`. Therefore, <i>each</i> `Inner` instance must have sufficient=
data to compute the location of its containing `Outer` object from just it=
s own `this` pointer.<br><br>So putting those offsets in the owning class s=
omewhere won't work.<br></div></blockquote><div><br>Wait, I think I und=
erstand sort of what you were getting at. It still won't work, but for =
a different reason.<br><br>You were basically saying that inner class imple=
mentations would assume that the four bytes preceeding its `this` pointer w=
ould contain the offset. And that it's up to how the individual owning =
class(es) lay those objects out to set those values. So you can copy indivi=
dual members just fine, since the offset isn't strictly part of `this`.=
And an instance can simply decrement `this` four bytes and get the offset,=
since instances of inner classes can only be created within another class.=
<br><br>The problem then is with arrays of inner classes. Earlier, we forba=
de arrays of empty classes because they don't make sense and would have=
to take up space. But arrays of stateful inner classes couldn't work w=
ith this either. The reason being that arrays have to be contiguous, that t=
he offset from one array entry to another must be `sizeof(T)`. So there'=
;s no room available for an offset.<br><br>Not unless that offset is <i>par=
t</i> of `sizeof(T)`...<br><br>So either way, the offset has to be a part o=
f the type.<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_958_775666694.1441392717567--
------=_Part_957_657483540.1441392717567--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Fri, 4 Sep 2015 11:53:02 -0700 (PDT)
Raw View
------=_Part_890_968435380.1441392782106
Content-Type: multipart/alternative;
boundary="----=_Part_891_1568836412.1441392782106"
------=_Part_891_1568836412.1441392782106
Content-Type: text/plain; charset=UTF-8
On Friday, September 4, 2015 at 2:51:57 PM UTC-4, Nicol Bolas wrote:
>
> On Friday, September 4, 2015 at 2:38:52 PM UTC-4, Nicol Bolas wrote:
>>
>> On Friday, September 4, 2015 at 1:51:56 PM UTC-4, Matthew Woehlke wrote:
>>>
>>> On 2015-09-04 12:51, Nicol Bolas wrote:
>>> > On Friday, September 4, 2015 at 10:08:48 AM UTC-4, Matthew Woehlke
>>> wrote:
>>> >> On 2015-09-03 22:44, Nicol Bolas wrote:
>>> >>> class Outer
>>> >>> {
>>> >>> <<inducer>> class Inner {...};
>>> >>> Inner a, b;
>>> >>> };
>>> >>>
>>> >>> Outer out;
>>> >>> out.b = out.a; //Explicitly invoke copy assignment.
>>> >>> out.b = std::move(out.a); //Explicitly invoke move assignment.
>>> >>>
>>> >>> Things like that should not be allowed.
>>> >>
>>> >> Why not? An *assignment operator* doesn't have anything to do with
>>> >> storage; the target object already exists, and the source object
>>> doesn't
>>> >> cease to exist. Even a move ctor is required to leave the source
>>> object
>>> >> in a *valid* state.
>>> >
>>> > Here's the problem. Remember what the actual inner class looks like:
>>> >
>>> > <<inducer>> class Inner
>>> > {
>>> > ...
>>> > int32_t __offset;
>>> > };
>>> >
>>> > Regardless of the specific information, each class member has its own
>>> > distinct offset, which goes from that class member's `this` to the
>>> owning
>>> > class's `this`.
>>> >
>>> > So `out.a.__offset` and `out.b.__offset` are different values.
>>> >
>>> > However, if Inner is actually trivially copyable, then that means you
>>> have
>>> > to be able to copy it via memcpy. The trivial copy
>>> constructor/assignment
>>> > can also be a memcpy.
>>>
>>> I think that the offset should/would not be accessible to the user, but
>>> always managed by the containing class.
>>>
>>
>> That's why I used the double-underscore on the member name. It's there
>> for exposition, but is an implementation detail that's not accessible by
>> the user.
>>
>> My point is that, user-accessible or not, it is part of `Inner`'s
>> storage. And there's no other way to implement it. After all, code may only
>> have a pointer/reference to the `Inner`. Therefore, *each* `Inner`
>> instance must have sufficient data to compute the location of its
>> containing `Outer` object from just its own `this` pointer.
>>
>> So putting those offsets in the owning class somewhere won't work.
>>
>
> Wait, I think I understand sort of what you were getting at. It still
> won't work, but for a different reason.
>
> You were basically saying that inner class implementations would assume
> that the four bytes preceeding its `this` pointer would contain the offset.
> And that it's up to how the individual owning class(es) lay those objects
> out to set those values. So you can copy individual members just fine,
> since the offset isn't strictly part of `this`. And an instance can simply
> decrement `this` four bytes and get the offset, since instances of inner
> classes can only be created within another class.
>
> The problem then is with arrays of inner classes. Earlier, we forbade
> arrays of empty classes because they don't make sense and would have to
> take up space. But arrays of stateful inner classes couldn't work with this
> either. The reason being that arrays have to be contiguous, that the offset
> from one array entry to another must be `sizeof(T)`. So there's no room
> available for an offset.
>
> Not unless that offset is *part* of `sizeof(T)`...
>
> So either way, the offset has to be a part of the type.
>
Of course, you could fix this by forbidding arrays of inner classes
altogether ;)
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_891_1568836412.1441392782106
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Friday, September 4, 2015 at 2:51:57 PM UTC-4, Nicol Bolas wrote:<blockq=
uote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-lef=
t: 1px #ccc solid;padding-left: 1ex;">On Friday, September 4, 2015 at 2:38:=
52 PM UTC-4, Nicol Bolas wrote:<blockquote class=3D"gmail_quote" style=3D"m=
argin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex">On F=
riday, September 4, 2015 at 1:51:56 PM UTC-4, Matthew Woehlke wrote:<blockq=
uote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:=
1px #ccc solid;padding-left:1ex">On 2015-09-04 12:51, Nicol Bolas wrote:
<br>> On Friday, September 4, 2015 at 10:08:48 AM UTC-4, Matthew Woehlke=
wrote:
<br>>> On 2015-09-03 22:44, Nicol Bolas wrote:=20
<br>>>> class Outer=20
<br>>>> {=20
<br>>>> =C2=A0 <<inducer>> class Inner {...};=20
<br>>>> =C2=A0 Inner a, b;=20
<br>>>> };=20
<br>>>>
<br>>>> Outer out;=20
<br>>>> out.b =3D out.a; //Explicitly invoke copy assignment.=20
<br>>>> out.b =3D std::move(out.a); //Explicitly invoke move assig=
nment.=20
<br>>>>
<br>>>> Things like that should not be allowed.=20
<br>>>
<br>>> Why not? An *assignment operator* doesn't have anything to=
do with=20
<br>>> storage; the target object already exists, and the source obje=
ct doesn't=20
<br>>> cease to exist. Even a move ctor is required to leave the sour=
ce object=20
<br>>> in a *valid* state.
<br>>=20
<br>> Here's the problem. Remember what the actual inner class looks=
like:
<br>>=20
<br>> <<inducer>> class Inner
<br>> {
<br>> ...
<br>> int32_t __offset;
<br>> };
<br>>=20
<br>> Regardless of the specific information, each class member has its =
own=20
<br>> distinct offset, which goes from that class member's `this` to=
the owning=20
<br>> class's `this`.
<br>>=20
<br>> So `out.a.__offset` and `out.b.__offset` are different values.
<br>>=20
<br>> However, if Inner is actually trivially copyable, then that means =
you have=20
<br>> to be able to copy it via memcpy. The trivial copy constructor/ass=
ignment=20
<br>> can also be a memcpy.
<br>
<br>I think that the offset should/would not be accessible to the user, but
<br>always managed by the containing class.
<br></blockquote><div><br>That's why I used the double-underscore on th=
e member name. It's there for exposition, but is an implementation deta=
il that's not accessible by the user.<br><br>My point is that, user-acc=
essible or not, it is part of `Inner`'s storage. And there's no oth=
er way to implement it. After all, code may only have a pointer/reference t=
o the `Inner`. Therefore, <i>each</i> `Inner` instance must have sufficient=
data to compute the location of its containing `Outer` object from just it=
s own `this` pointer.<br><br>So putting those offsets in the owning class s=
omewhere won't work.<br></div></blockquote><div><br>Wait, I think I und=
erstand sort of what you were getting at. It still won't work, but for =
a different reason.<br><br>You were basically saying that inner class imple=
mentations would assume that the four bytes preceeding its `this` pointer w=
ould contain the offset. And that it's up to how the individual owning =
class(es) lay those objects out to set those values. So you can copy indivi=
dual members just fine, since the offset isn't strictly part of `this`.=
And an instance can simply decrement `this` four bytes and get the offset,=
since instances of inner classes can only be created within another class.=
<br><br>The problem then is with arrays of inner classes. Earlier, we forba=
de arrays of empty classes because they don't make sense and would have=
to take up space. But arrays of stateful inner classes couldn't work w=
ith this either. The reason being that arrays have to be contiguous, that t=
he offset from one array entry to another must be `sizeof(T)`. So there'=
;s no room available for an offset.<br><br>Not unless that offset is <i>par=
t</i> of `sizeof(T)`...<br><br>So either way, the offset has to be a part o=
f the type.<br></div></blockquote><div><br>Of course, you could fix this by=
forbidding arrays of inner classes altogether ;)<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_891_1568836412.1441392782106--
------=_Part_890_968435380.1441392782106--
.
Author: John Yates <john@yates-sheets.org>
Date: Fri, 4 Sep 2015 15:40:36 -0400
Raw View
--047d7b66f9f7ef64f6051ef11271
Content-Type: text/plain; charset=UTF-8
On Fri, Sep 4, 2015 at 2:53 PM, Nicol Bolas <jmckesson@gmail.com> wrote:
> Of course, you could fix this by forbidding arrays of inner classes
> altogether ;)
>
Why the smiley?
I would happily live with that restriction. It is not like a lack of
arrays of inner classes renders unimplementable something that I otherwise
might have been able to express. It just means that in this obscure corner
of implementation I have to fallback on pre-inner-class methodologies. So
what?
/john
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--047d7b66f9f7ef64f6051ef11271
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div>On Fri, Sep 4, 2015 at 2:53 PM, Nicol Bolas <span dir=
=3D"ltr"><<a href=3D"mailto:jmckesson@gmail.com" target=3D"_blank">jmcke=
sson@gmail.com</a>></span> wrote:<br></div><div class=3D"gmail_extra"><d=
iv class=3D"gmail_quote"><blockquote class=3D"gmail_quote" style=3D"margin:=
0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);=
border-left-style:solid;padding-left:1ex"><div class=3D""><div class=3D"h5"=
><span style=3D"color:rgb(34,34,34)">Of course, you could fix this by forbi=
dding arrays of inner classes altogether ;)</span></div></div></blockquote>=
<div><br></div>Why the smiley?</div><div class=3D"gmail_quote"><br></div><d=
iv class=3D"gmail_quote">I would happily live with that restriction.=C2=A0 =
It is not like a lack of arrays of inner classes renders unimplementable so=
mething that I otherwise might have been able to express.=C2=A0 It just mea=
ns that in this obscure corner of implementation I have to fallback on pre-=
inner-class methodologies.=C2=A0 So what?<div><br></div><div>/john</div><di=
v>=C2=A0</div><div><br></div></div></div></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--047d7b66f9f7ef64f6051ef11271--
.
Author: Miro Knejp <miro.knejp@gmail.com>
Date: Fri, 4 Sep 2015 22:01:09 +0200
Raw View
This is a multi-part message in MIME format.
--------------090700090904010202060303
Content-Type: text/plain; charset=UTF-8; format=flowed
Am 04.09.2015 um 20:38 schrieb Nicol Bolas:
>
> Trivially copyable is /behavior/; it's something you have to specify
> in the standard. And if that behavior cannot be implemented, then that
> behavior cannot be permitted by the standard.
>
> So it's not an implementation detail. The need to implement it is
> getting in the way of the desired behavior.
The let's try to specify it.
The problem here is, that the nested instance itself (if it has an
offset) is not trivially copyable, but it doesn't necessarily prevent
the parent fom being. Further, an offset member is only necessary if the
nested class accesses non-static members of the parent.
So let me try:
* An empty nested class is TriviallyCopyable under the same rules as if
it were a regular class (assuming empty inline classes all have the same
"this" pointer as the parent and thus cannot form arrays).
* A non-empty nested class is not TriviallyCopyable if any of its
non-static members access non-static members of the parent. This can
only be determined if all member functions are defined inline, otherwise
the compiler has to assume it needs an offset.
* If a nested class would fulfill all requirements for TriviallyCopyable
if it were a regular class, it doesn't prevent the parent class from
being TriviallyCopyable.
However, I have to point out that the offset-based approach isn't what
I'd call "zero overhead", neither in storage nor runtime. This can be
implemented alredy by storing the this-offset instead of a this-pointer
to the parent and doing the pointer-arithmetic yourself with the
appropriate amount of casting. It's not pretty but it works. I'd like to
see a way to do *true* zero-overhead inline classes, as that is
currently not possible (except for weird union hacks with questionable
portability), hence my approach involved type manipulation. I understand
the opposition, even though I don't necessarily agree with all of it and
am still looking for an easier way.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--------------090700090904010202060303
Content-Type: text/html; charset=UTF-8
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
<body bgcolor="#FFFFFF" text="#000000">
Am 04.09.2015 um 20:38 schrieb Nicol Bolas:<br>
<blockquote
cite="mid:63743c60-0219-448d-9e96-e685fd1f6c1b@isocpp.org"
type="cite"><br>
<div>Trivially copyable is <i>behavior</i>; it's something you
have to specify in the standard. And if that behavior cannot be
implemented, then that behavior cannot be permitted by the
standard.<br>
<br>
So it's not an implementation detail. The need to implement it
is getting in the way of the desired behavior.<br>
</div>
</blockquote>
The let's try to specify it.<br>
The problem here is, that the nested instance itself (if it has an
offset) is not trivially copyable, but it doesn't necessarily
prevent the parent fom being. Further, an offset member is only
necessary if the nested class accesses non-static members of the
parent.<br>
<br>
So let me try:<br>
* An empty nested class is TriviallyCopyable under the same rules as
if it were a regular class (assuming empty inline classes all have
the same "this" pointer as the parent and thus cannot form arrays).<br>
* A non-empty nested class is not TriviallyCopyable if any of its
non-static members access non-static members of the parent. This can
only be determined if all member functions are defined inline,
otherwise the compiler has to assume it needs an offset.<br>
* If a nested class would fulfill all requirements for
TriviallyCopyable if it were a regular class, it doesn't prevent the
parent class from being TriviallyCopyable.<br>
<br>
However, I have to point out that the offset-based approach isn't
what I'd call "zero overhead", neither in storage nor runtime. This
can be implemented alredy by storing the this-offset instead of a
this-pointer to the parent and doing the pointer-arithmetic yourself
with the appropriate amount of casting. It's not pretty but it
works. I'd like to see a way to do *true* zero-overhead inline
classes, as that is currently not possible (except for weird union
hacks with questionable portability), hence my approach involved
type manipulation. I understand the opposition, even though I don't
necessarily agree with all of it and am still looking for an easier
way.<br>
</body>
</html>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an email to <a href="mailto:std-proposals+unsubscribe@isocpp.org">std-proposals+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href="mailto:std-proposals@isocpp.org">std-proposals@isocpp.org</a>.<br />
Visit this group at <a href="http://groups.google.com/a/isocpp.org/group/std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/</a>.<br />
--------------090700090904010202060303--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Fri, 04 Sep 2015 16:40:12 -0400
Raw View
On 2015-09-04 16:01, Miro Knejp wrote:
> Further, an offset member is only necessary if the nested class
> accesses non-static members of the parent.
Wrong. The compiler must know the size of the class from *class*
definition (e.g. foo.h), which may not contain definitions of the class
*members* (e.g foo.cpp). Therefore, the compiler *must* assume that such
access occurs.
Even if all methods are inline, the notion that changing the code in a
member function can change the size of a type... just... no.
> * An empty nested class is TriviallyCopyable under the same rules as if
> it were a regular class (assuming empty inline classes all have the same
> "this" pointer as the parent and thus cannot form arrays).
An empty (inline) class (inner or not) cannot be constructed or
destroyed, period, and is irrelevant to whether any object having one as
a member is trivially copyable.
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Fri, 04 Sep 2015 16:41:50 -0400
Raw View
On 2015-09-04 14:38, Nicol Bolas wrote:
> On Friday, September 4, 2015 at 1:51:56 PM UTC-4, Matthew Woehlke wrote:
>> On 2015-09-04 12:51, Nicol Bolas wrote:
>>> So... what happens if you do `memcpy(&out.b, &out.a,
>>> sizeof(Outer::Inner))`? That's right, you copy `out.a.__offset` into
>>> `out.b.__offset`. So now `out.b` has the wrong offset.
>>>
>>> That's bad.
>>
>> So... don't do that :-). That's clearly UB, same as if you wrote
>> directly into a class's vptr or other such nonsense.
>
> Ah, but there's the difference: classes with vptrs are not trivially
> copyable. If you're saying that you can't memcpy it, then it's not
> trivially copyable (ie: the entire point of the trivially copyable notion).
> Which is something we don't want to do unless there's no other choice.
>
>> The compiler should have no problem making the necessary adjustments so
>> that a relocating copy ctor for an inner class does not clobber the offset.
>
> Absolutely. But it would not be a *trivial* copy constructor
> <http://en.cppreference.com/w/cpp/language/copy_constructor#Trivial_copy_constructor>
> anymore.
Why not? The offset is an implementation detail, therefore it is not a
member for the purpose of standardese. The trick is to specify that when
the compiler performs a trivial copy of an inner class instance to a
*different* instance of the same type (e.g. in a default copy ctor),
that the offset bytes must be excluded from that copy.
I'm still not convinced this is a(n insurmountable) problem. If it's the
*same* member (but of a different containing instance), then the offset
is the same and can be copied also, so the case of the outer class being
trivially copied is not broken. If it's a *different* member, you just
can't group doing copies of multiple consecutive inner classes into a
single memmove. However, how would you ever even get into a situation
where non-user-written code would be trying to do that? (Remember,
relocatability usually comes into play in dynamic arrays, but you can't
have a dynamic array of inner classes...)
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Miro Knejp <miro.knejp@gmail.com>
Date: Fri, 4 Sep 2015 22:57:09 +0200
Raw View
Am 04.09.2015 um 22:40 schrieb Matthew Woehlke:
> On 2015-09-04 16:01, Miro Knejp wrote:
>> Further, an offset member is only necessary if the nested class
>> accesses non-static members of the parent.
> Wrong. The compiler must know the size of the class from *class*
> definition (e.g. foo.h), which may not contain definitions of the class
> *members* (e.g foo.cpp). Therefore, the compiler *must* assume that such
> access occurs.
Seriously? Just two lines further down I even mention that "This can
only be determined if all member functions are defined inline, otherwise
the compiler has to assume it needs an offset."
>
> Even if all methods are inline, the notion that changing the code in a
> member function can change the size of a type... just... no.
Probably true. If you never access the parent you can just as well use a
regular subobject.
>
>> * An empty nested class is TriviallyCopyable under the same rules as if
>> it were a regular class (assuming empty inline classes all have the same
>> "this" pointer as the parent and thus cannot form arrays).
> An empty (inline) class (inner or not) cannot be constructed or
> destroyed, period, and is irrelevant to whether any object having one as
> a member is trivially copyable.
>
Why not? If you call it a class it should support user-defined special
member functions. Just because it doesn't have non-static state doesn't
mean it cannot have construction/destruction/copy side effects. Sounds
like an arbitrary restriction to me.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Sat, 5 Sep 2015 06:23:20 -0700 (PDT)
Raw View
------=_Part_637_684302534.1441459400596
Content-Type: multipart/alternative;
boundary="----=_Part_638_873546124.1441459400596"
------=_Part_638_873546124.1441459400596
Content-Type: text/plain; charset=UTF-8
On Friday, September 4, 2015 at 4:45:09 PM UTC-4, Matthew Woehlke wrote:
>
> On 2015-09-04 14:38, Nicol Bolas wrote:
> > On Friday, September 4, 2015 at 1:51:56 PM UTC-4, Matthew Woehlke wrote:
> >> On 2015-09-04 12:51, Nicol Bolas wrote:
> >>> So... what happens if you do `memcpy(&out.b, &out.a,
> >>> sizeof(Outer::Inner))`? That's right, you copy `out.a.__offset` into
> >>> `out.b.__offset`. So now `out.b` has the wrong offset.
> >>>
> >>> That's bad.
> >>
> >> So... don't do that :-). That's clearly UB, same as if you wrote
> >> directly into a class's vptr or other such nonsense.
> >
> > Ah, but there's the difference: classes with vptrs are not trivially
> > copyable. If you're saying that you can't memcpy it, then it's not
> > trivially copyable (ie: the entire point of the trivially copyable
> notion).
> > Which is something we don't want to do unless there's no other choice.
> >
> >> The compiler should have no problem making the necessary adjustments so
> >> that a relocating copy ctor for an inner class does not clobber the
> offset.
> >
> > Absolutely. But it would not be a *trivial* copy constructor
> > <
> http://en.cppreference.com/w/cpp/language/copy_constructor#Trivial_copy_constructor>
>
> > anymore.
>
> Why not? The offset is an implementation detail, therefore it is not a
> member for the purpose of standardese.
Yes, and vtable pointers are also an implementation detail, one that is
"not a member for the purpose of standardese". And vtable pointers are
*exactly* why virtual functions/base classes break trivial copyability. It
allows implementations the freedom to insert those implementation details
into the class itself.
The same seems to be true here.
> The trick is to specify that when
> the compiler performs a trivial copy of an inner class instance to a
> *different* instance of the same type (e.g. in a default copy ctor),
> that the offset bytes must be excluded from that copy.
>
Trivial copying is trivial copying; by definition, it *cannot* include
specialized logic to exclude certain bits. That sort of thing would make it
non-trivial. Again, this is why virtual classes aren't trivially copyable:
because the compiler has to insert specialized code to prevent modifying
any implementation details during the copy. And thus, the copy cannot be
trivial.
If the copy constructor has to be anything other than `memcpy(dest, src,
sizeof(T))`, then it's non-trivial.
So only implementation where trivial copying between different members of
the same inner class type would work is if the inner class itself does not
contain the offset. Which returns to the aforementioned problem: how do you
convert a pointer to the inner class member into a pointer to the owning
instance, if the only information you have is the pointer to the inner
class member?
Laying out the owning class such that it naturally stores an offset to
non-empty inner classes is a functional implementation, but it also means
you can't have arrays of inner classes.
I'm still not convinced this is a(n insurmountable) problem. If it's the
> *same* member (but of a different containing instance), then the offset
> is the same and can be copied also, so the case of the outer class being
> trivially copied is not broken. If it's a *different* member, you just
> can't group doing copies of multiple consecutive inner classes into a
> single memmove.
It doesn't matter how many are being copied, one or twenty. If it can't be
copied with a memcpy, it's not trivially copyable.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_638_873546124.1441459400596
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<br><br>On Friday, September 4, 2015 at 4:45:09 PM UTC-4, Matthew Woehlke w=
rote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8e=
x;border-left: 1px #ccc solid;padding-left: 1ex;">On 2015-09-04 14:38, Nico=
l Bolas wrote:
<br>> On Friday, September 4, 2015 at 1:51:56 PM UTC-4, Matthew Woehlke =
wrote:
<br>>> On 2015-09-04 12:51, Nicol Bolas wrote:=20
<br>>>> So... what happens if you do `memcpy(&out.b, &out.=
a,=20
<br>>>> sizeof(Outer::Inner))`? That's right, you copy `out.a.=
__offset` into=20
<br>>>> `out.b.__offset`. So now `out.b` has the wrong offset.=20
<br>>>>
<br>>>> That's bad.=20
<br>>>
<br>>> So... don't do that :-). That's clearly UB, same as if=
you wrote=20
<br>>> directly into a class's vptr or other such nonsense.
<br>>=20
<br>> Ah, but there's the difference: classes with vptrs are not tri=
vially=20
<br>> copyable. If you're saying that you can't memcpy it, then =
it's not=20
<br>> trivially copyable (ie: the entire point of the trivially copyable=
notion).=20
<br>> Which is something we don't want to do unless there's no o=
ther choice.
<br>> =C2=A0
<br>>> The compiler should have no problem making the necessary adjus=
tments so=20
<br>>> that a relocating copy ctor for an inner class does not clobbe=
r the offset.
<br>>=20
<br>> Absolutely. But it would not be a *trivial* copy constructor=20
<br>> <<a href=3D"http://en.cppreference.com/w/cpp/language/copy_cons=
tructor#Trivial_copy_constructor" target=3D"_blank" rel=3D"nofollow" onmous=
edown=3D"this.href=3D'http://www.google.com/url?q\75http%3A%2F%2Fen.cpp=
reference.com%2Fw%2Fcpp%2Flanguage%2Fcopy_constructor%23Trivial_copy_constr=
uctor\46sa\75D\46sntz\0751\46usg\75AFQjCNFTv6690QlBanDVXMMyICD-Gwo5eg';=
return true;" onclick=3D"this.href=3D'http://www.google.com/url?q\75htt=
p%3A%2F%2Fen.cppreference.com%2Fw%2Fcpp%2Flanguage%2Fcopy_constructor%23Tri=
vial_copy_constructor\46sa\75D\46sntz\0751\46usg\75AFQjCNFTv6690QlBanDVXMMy=
ICD-Gwo5eg';return true;">http://en.cppreference.com/w/<wbr>cpp/languag=
e/copy_constructor#<wbr>Trivial_copy_constructor</a>>
<br>> anymore.
<br>
<br>Why not? The offset is an implementation detail, therefore it is not a
<br>member for the purpose of standardese.</blockquote><div><br>Yes, and vt=
able pointers are also an implementation detail, one that is "not a
member for the purpose of standardese". And vtable pointers are <i>exa=
ctly</i> why virtual functions/base classes break trivial copyability. It a=
llows implementations the freedom to insert those implementation details in=
to the class itself.<br><br>The same seems to be true here.<br>=C2=A0</div>=
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;">The trick is to specify that w=
hen
<br>the compiler performs a trivial copy of an inner class instance to a
<br>*different* instance of the same type (e.g. in a default copy ctor),
<br>that the offset bytes must be excluded from that copy.<br></blockquote>=
<div><br>Trivial copying is trivial copying; by definition, it <i>cannot</i=
> include specialized logic to exclude certain bits. That sort of thing wou=
ld make it non-trivial. Again, this is why virtual classes aren't trivi=
ally copyable: because the compiler has to insert specialized code to preve=
nt modifying any implementation details during the copy. And thus, the copy=
cannot be trivial.<br><br>If the copy constructor has to be anything other=
than `memcpy(dest, src, sizeof(T))`, then it's non-trivial.<br><br>So =
only implementation where trivial copying between different members of the =
same inner class type would work is if the inner class itself does not cont=
ain the offset. Which returns to the aforementioned problem: how do you con=
vert a pointer to the inner class member into a pointer to the owning insta=
nce, if the only information you have is the pointer to the inner class mem=
ber?<br><br>Laying out the owning class such that it naturally stores an of=
fset to non-empty inner classes is a functional implementation, but it also=
means you can't have arrays of inner classes.<br><br></div><blockquote=
class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1=
px #ccc solid;padding-left: 1ex;">
I'm still not convinced this is a(n insurmountable) problem. If it'=
s the
<br>*same* member (but of a different containing instance), then the offset
<br>is the same and can be copied also, so the case of the outer class bein=
g
<br>trivially copied is not broken. If it's a *different* member, you j=
ust
<br>can't group doing copies of multiple consecutive inner classes into=
a
<br>single memmove.</blockquote><div><br>It doesn't matter how many are=
being copied, one or twenty. If it can't be copied with a memcpy, it&#=
39;s not trivially copyable.</div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_638_873546124.1441459400596--
------=_Part_637_684302534.1441459400596--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Sun, 6 Sep 2015 16:04:11 -0700 (PDT)
Raw View
------=_Part_2922_311081620.1441580651504
Content-Type: multipart/alternative;
boundary="----=_Part_2923_21863751.1441580651505"
------=_Part_2923_21863751.1441580651505
Content-Type: text/plain; charset=UTF-8
OK, given the various discussions on this thread, I've put together an
alternate look at this, one which separates empty types from inner classes
and addresses most of the questions around stateful inner classes. I hope
that it spells out all of the various interactions of inner classes, to
make sure that there is resolution on how they work. I discovered a few
holes while I was describing behavior (constructors and whether they can
access owned object internals), so I tried to plug them as best I could.
I also found an interesting question with regard to stateless classes and
aggregate initialization. I'm not really sure what the answer is or should
be.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_2923_21863751.1441580651505
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">OK, given the various discussions on this thread, I've=
put together an alternate look at this, one which separates empty types fr=
om inner classes and addresses most of the questions around stateful inner =
classes. I hope that it spells out all of the various interactions of inner=
classes, to make sure that there is resolution on how they work. I discove=
red a few holes while I was describing behavior (constructors and whether t=
hey can access owned object internals), so I tried to plug them as best I c=
ould.<br><br>I also found an interesting question with regard to stateless =
classes and aggregate initialization. I'm not really sure what the answ=
er is or should be.<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_2923_21863751.1441580651505--
------=_Part_2922_311081620.1441580651504
Content-Type: text/html; charset=UTF-8;
name="Stateless And Inner Classes.html"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="Stateless And Inner Classes.html"
X-Attachment-Id: 31d73a3c-83bb-4d7c-9085-87f589552c59
Content-ID: <31d73a3c-83bb-4d7c-9085-87f589552c59>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.=
w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns=3D"http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf-8" =
/>
<meta http-equiv=3D"Content-Style-Type" content=3D"text/css" />
<meta name=3D"generator" content=3D"pandoc" />
<title></title>
<style type=3D"text/css">code{white-space: pre;}</style>
</head>
<body>
<h1 id=3D"stateless-and-inner-classes">Stateless and Inner Classes</h1>
<p>This proposal describes two separate concepts, but they each contribute =
useful functionality to the other.</p>
<h2 id=3D"stateless-classes">Stateless Classes</h2>
<p>Conceptually, a stateless class is a class which takes up no space when =
used as a direct member of another class. The purpose of this feature is to=
essentially require empty member optimization, but only where specified ex=
plicitly.</p>
<h3 id=3D"definition">Definition</h3>
<p>This proposal does not yet suggest any particular syntax for defining a =
stateless class. The shorthand for this will be:</p>
<pre><code><<stateless>> class Classname { };</code></pre>
<p>All uses of a stateless class, where the statelessness of the class woul=
d actually matter, require the class definition. Therefore, the stateless c=
lass syntax does not need to be available for forward declarations of a cla=
ss. Also, whatever stateless syntax is used, it should be able to be used w=
ith anonymous class definitions.</p>
<p>Stateless class definitions:</p>
<ul>
<li>May not derive from any non-stateless type.</li>
<li>May not have NSDM's of non-stateless types.</li>
<li>May not have virtual functions.</li>
<li>May not have virtual base classes.</li>
</ul>
<p>[note: These properties mean that a stateless class is always standard l=
ayout.]</p>
<p>In all other ways, a stateless class definition is equivalent to a non-s=
tateless class definition.</p>
<h3 id=3D"usage-of-types">Usage of Types</h3>
<p>The <code>sizeof</code> a stateless class is not affected by the fact th=
at it is stateless. [note: This means you get what you would for any empty =
class.]</p>
<p>The standard library should have a template metafunction (and variable) =
for testing if a type is a stateless class.</p>
<p>Stateless classes can be used in exactly the same ways as regular classe=
s with the following exceptions:</p>
<ul>
<li>Variables which are arrays of stateless types cannot be declared.<a hre=
f=3D"#fn1" class=3D"footnoteRef" id=3D"fnref1"><sup>1</sup></a> Arrays of s=
tateless types can still be allocated with <code>new[]</code>.</li>
</ul>
<p>If a class declares a NSDM of a stateless class type, or non-virtually d=
erives from a stateless class type, the size of the class will not be affec=
ted by the presence of this member/base class. Stateless members/base class=
es do not affect the layout of members within the object. [note: For exampl=
e, if a class has all of its regular members public, it may have a private =
stateless member without affecting whether it is <a href=3D"http://en.cppre=
ference.com/w/cpp/concept/StandardLayoutType">standard layout</a>.] Statele=
ss members/base classes can affect whether the class is <a href=3D"http://e=
n.cppreference.com/w/cpp/concept/TrivialType">trivial</a> or <a href=3D"htt=
p://en.cppreference.com/w/cpp/concept/TriviallyCopyable">trivially copyable=
</a> as normal. [note: If the stateless class is not trivially-copyable, th=
en classes that use them will not be as well.]</p>
<p><strong>Question:</strong> Do we want stateless class NSDM's to take par=
t in aggregate initialization? If we want stateless class members to effect=
ively be transparent, to purely be implementation details, then we probably=
don't want them to be part of aggregate initialization. At the same time, =
stateless classes still exist and can still have constructors.</p>
<h3 id=3D"stateless_impl">On Implementation</h3>
<p>Implementing stateless types should be easy in the compiler. A more diff=
icult question is within the language.</p>
<p>Objects are defined as a region of memory. And while stateless types do =
have some region of memory, that region may be shared by other, unrelated t=
ypes. A type that has multiple stateless members will likely give them all =
the same pointer value (a pointer to their owning type).</p>
<p>This already comes up with regard to the <a href=3D"http://en.cppreferen=
ce.com/w/cpp/language/ebo">empty base optimization</a>. When this optimizat=
ion is employed, all empty base classes have the same pointer address. Stan=
dard layout rules require EBO even for multiple inheritance with empty base=
classes, which means multiple seemingly unrelated types can end up with th=
e same pointer address, refering to the same region of memory.</p>
<p>So the standard already deals with the concept of multiple, disparate ob=
jects in the same memory. Implementing stateless members only requires that=
the standard handle them at the NSDM level.</p>
<h2 id=3D"inner-classes">Inner Classes</h2>
<p>The Java concept of an inner class is a nested class, all objects of whi=
ch are bound to some instance of the containing class. The inner class can =
access NSDM's of the containing class as easily as it accesses its own. In =
this way, an inner class is effectively an extension of the outer class.</p=
>
<p>This proposed C++ concept is similar (hence the name), but it does not a=
llow for dynamic allocation of such objects the way one can in Java.<a href=
=3D"#fn2" class=3D"footnoteRef" id=3D"fnref2"><sup>2</sup></a></p>
<h3 id=3D"definition-1">Definition</h3>
<p>Inner classes can only be defined in the scope of a class; this class is=
called the <em>owning class</em>. [note: The owning class may itself be an=
inner class] This proposal does not yet suggest any particular syntax for =
defining inner classes. The shorthand for such definitions will be:</p>
<pre><code><<inner>> class Classname { };</code></pre>
<p>Uses of inner classes that require the compiler to know that it is an in=
ner class also requires the full definition of the inner class. As such, th=
e inner class syntax does not have to be used in the class declaration; jus=
t the definition. However, the inner class syntax must also allow for anony=
mous inner class definitions.</p>
<p>Also, such syntax should be able to be used with the above stateless syn=
tax. Neither implies the other, but it should be possible to use both when =
so desired.</p>
<p>A class may only be derived from an inner class types if that class is i=
tself an inner class. Also, when deriving from an inner class, the owning c=
lass of the derived class must be either the same class as the base class's=
owning class or a class non-virtually inherited<a href=3D"#fn3" class=3D"f=
ootnoteRef" id=3D"fnref3"><sup>3</sup></a> from it. When deriving from an i=
nner class type, the inheritance cannot be virtual.<a href=3D"#fn4" class=
=3D"footnoteRef" id=3D"fnref4"><sup>4</sup></a></p>
<p>Inner classes may derive from any available type, as normal. They may ha=
ve any members, as normal, and they can use all other types, as normal.</p>
<h3 id=3D"usage-of-types-1">Usage of Types</h3>
<p>Variables of inner class types can only be declared as NSDM's of a class=
.. And the class which they are declared within must be either:</p>
<ul>
<li>The direct owner of the inner class.</li>
<li>A class non-virtually inherited<a href=3D"#fn5" class=3D"footnoteRef" i=
d=3D"fnref5"><sup>5</sup></a> from the direct owner of the inner class.</li=
>
</ul>
<p>Inner class types cannot be used as the type in <code>new</code> express=
ions, nor can temporaries be made of them. Objects of inner class types can=
not have their destructors explicitly called.</p>
<p>The standard library should have a template metafunction (and variable) =
for testing if a type is an inner class.</p>
<p>Pointers and references to inner class types work as normal for any type=
..</p>
<p>Questions regarding layout compatibility, trivial copyability, and certa=
in specific uses for inner classes are discussed in the <a href=3D"#inner_i=
mpl">implementation</a> section. This is because these behaviors materially=
affect how the compiler actually makes these things work. The behavior, ba=
sed on looking at the limits of implementations, is as follows:</p>
<ul>
<li>Stateless inner class members do not affect layout compatibility.</li>
<li>Inner class members, stateless or not, affect trivial copyability in th=
e same way as any other type. [note: The class declaring the member is not =
trivially copyable only if the member type is not trivially copyable.]</li>
<li>Objects which have stateful inner class members cannot be trivial types=
.. [note: They can still be trivially copyable.]</li>
<li>Inner class members can be copied/moved exactly as any other type. [not=
e: Specifically, if a stateful inner class member is trivially copyable, it=
should be legal to do a <code>memcpy(dest, src, sizeof(T))</code> on it in=
to another inner class member of the same type.]</li>
</ul>
<p>In all other ways, inner class types work just like regular types.</p>
<h3 id=3D"names-and-access">Names and Access</h3>
<p>The inner class of a class is implicitly friends of its owning class cla=
ss. This happens recursively up the inner class hierarchy. [note: An inner =
class of an inner class is friends with both its immediately owning class a=
nd the class that owns that one.]</p>
<p>Name lookup through inner classes can retrieve members of their owning c=
lasses as though they were derived from them. However, actual base classes =
have priority over owning class, when names conflict. The more recent owner=
s have priority over less recent owning classes.</p>
<p>Pointers and references to inner class members can be converted to point=
to their owning class instance via <code>static_cast</code>. Such conversi=
ons can also be implicit, much like converting from a derived class to a ba=
se class. [note: The reverse conversion is not allowed. You have to access =
the specific member yourself.]</p>
<p>If a pointer/reference to an inner class names a member of an owning cla=
ss (accessibility willing) and attempts to access it through that pointer/r=
eference, the pointer/reference will first be converted to the proper type =
(and value) via <code>static_cast</code>. [note: This means that one can ac=
tually access the public members of an owning class through just a pointer/=
reference to the inner class. But not the private ones, because that breaks=
accessibility.]</p>
<p>The exceptions to this access are within inner class constructors, NSDM =
initializers, and destructors. Because inner classes must be members, their=
owning class may not have been constructed yet. [note: If the object is a =
member of a class derived from its owning type, then the owning type will b=
e constructed before the inner class member.] As such, they should not be a=
ble to modify their owning object before they have been fully constructed. =
Nor should they be able to modify their owning object in their destructor, =
since the lifetime of that object has ended.</p>
<h3 id=3D"inner_impl">On Implementations</h3>
<p>Implementing inner classes is something of a concern. In Java, this is t=
rivial; just stick a <code>this</code> pointer into the inner class object.=
Garbage collection makes everything work out.</p>
<p>Such an implementation is possible in C++ (minus the GC). Indeed, there =
are class frameworks that implement this today. However, such implementatio=
ns cause a number of problems, beyond the syntactic difficulties of using t=
hose frameworks:</p>
<ul>
<li>It breaks trivial copyability, as the hidden <code>this</code> pointer =
shouldn't be copied or moved.</li>
<li>It introduces overhead where none might be necessary.</li>
</ul>
<p>Only a compiler is capable of implementing inner classes without these f=
laws (where possible). Even so, compiler implementations lead to some probl=
ems.</p>
<p>It is possible to implement stateless inner classes with zero overhead i=
n the owning class. This could be done easily enough by having the pointer =
to the stateless inner class member be the same pointer value as its owning=
object. This allows the compiler to instantly know how to convert from an =
inner class instance to its owning instance.</p>
<p>This would work even when the stateless inner class is a member of a der=
ived class of its owner. The compiler can statically convert the pointer to=
the derived class to a pointer to the base class (since virtual inheritanc=
e is not allowed) when accessing the inner class member.</p>
<p>Then, there is this even more complex case:</p>
<pre><code>class Base
{
public:
<<inner>> <<stateless> class In1
{ int GetBase() const {return baseVar;} };
private:
int baseVar;
};
class Derived : public Base;
{
public:
<<inner>> <<stateless>> class In2 : public Base=
::In1
{
int GetBase2() const {return GetBase();}
};
=20
In2 acc;
=20
private: =20
int deriVar;
};</code></pre>
<p>The key to remember here is that both <code>Base</code> and <code>Derive=
d</code> have non-stateless NSDMs, so they both take up space. And thus, th=
e pointer to <code>Base</code> is not required to be the same as the pointe=
r to <code>Derived</code>.</p>
<p>Even with this complexity, things still work. For <code>In2::GetBase2</c=
ode>, the compiler recognizes that <code>GetBase</code> is in the base clas=
s of <code>Derived::In2</code>. But it also realizes that this base class i=
s an inner class. So it converts the <code>this</code> pointer of type <cod=
e>Derived::In2*</code> into a <code>Derived*</code>, then converts it into =
a <code>Base*</code>, then converts it into an <code>Base::In1*</code>. The=
same thing happens for calling <code>acc.GetBase</code> directly, or any e=
xpression that converts an <code>Derived::In2*</code> into <code>Base::In1*=
</code>.</p>
<p>All of these conversions are static, based on compile-time offsets. Ther=
efore, one can obtain a pointer/reference to <code>acc</code> and manipulat=
e it. The compiler knows the type of the object, and it can see that it is =
an inner class type, and do the appropriate math to make it all work.</p>
<p>Where implementations get complicated is with stateful inner classes. Be=
cause each member has its own state, the conversion from <code>Outer::Inner=
*</code> to <code>Outer*</code> is no longer a simple typecast. It depends =
on exactly where that member is within its object.</p>
<p>Therefore, for each stateful inner class member, the compiler must have =
access to some data (typically a byte offset) which is used to convert <cod=
e>this</code> from <code>Outer::Inner*</code> to <code>Outer*</code>. For m=
ultiple nestings of stateful inner classes, each level has its own offset t=
o get to the next level.</p>
<p>The question is where this offset is stored.</p>
<p>One thing is certain: the offset must be stored someplace that is access=
ible with just a pointer to <code>this</code>. After all, users may have on=
ly a pointer/reference to an inner class member, and they have every reason=
to expect that this will function. In such a case, the only pieces of info=
rmation the compiler has are the class definitions and <code>this</code>.</=
p>
<p>We are left with two alternative implementations. The offset could be st=
ored within the inner class itself, in a hidden member (ala vtable pointers=
). Or the offset could be stored in the direct owning class, in memory that=
is directly adjacent to the instance.</p>
<p>Both of these have behavioral consequences. Both solutions break standar=
d layout, as a consequence of having to insert additional offsets. But this=
is to be expected.</p>
<p><strong>Option 1: In-Object Memory:</strong> This is the most obvious im=
plementation strategy. The offset is simply a hidden field of the inner cla=
ss.</p>
<p>The downside of this approach is that stateful inner classes <em>cannot<=
/em> be trivially copied. This is for similar reasons as to why types with =
virtual functions/base classes cannot be trivially copied. The value of the=
offset is based on the specific member variable and where it is within its=
owning type. As such, copying it between two <em>different</em> members (o=
f the same type) is really bad. And since the offset is part of the type, y=
ou can't copy the two types with a simple <code>memcpy(dest, src, sizeof(T)=
)</code>. And therefore, they are not trivially copyable.</p>
<p><strong>Option 2: Adjacent Memory:</strong> If the offset is in memory a=
djacent to the object, then fetching the offset with only <code>this</code>=
is easy: simply increment/decrement the pointer so that it points to the a=
djacent memory. The class definition will tell the compiler if it is statef=
ul, and if it is, then it knows it needs to do this work to get the offset.=
</p>
<p>However, this breaks the ability to have arrays of stateful inner class =
members. The reason being that arrays in C++ are required to be contiguous,=
that the pointer distance from one array element to another must be exactl=
y <code>sizeof(Class)</code>.</p>
<p>Note that arrays of stateless inner classes are forbidden because arrays=
of stateless classes period are forbidden. So forbidding arrays of inner c=
lasses of any kind is not too far afield.</p>
<p>The adjacent memory method preserves trivial copyability because the off=
set is where it needs to be: associated with the type that actually defines=
that offset. The offset is defined by the arrangement of the members of th=
e outer class. So the outer class is the one that stores the offset. The ou=
ter class can still be trivially copied because the offset for each member =
is a static property, not a dynamic one.</p>
<p>The outer class cannot be <em>trivial</em> however, since its default co=
nstructor (and every other non-copy/move constructor) will need to initiali=
ze this hidden data.</p>
<p>Trivial copyability is probably more generally useful than the ability t=
o aggregate stateful inner classes into arrays.</p>
<h2 id=3D"uses-and-deficiencies">Uses and Deficiencies</h2>
<p>Empty member optimization, and guaranteed empty base optimization outsid=
e of standard layout, has been something that users have wanted for quite s=
ome time.</p>
<p>Inner classes can be used for properties.</p>
<p>One of the biggest downsides of this approach is the difficulty in shari=
ng implementations of inner classes across disparate owning classes. One ca=
n use a template base class and the <a href=3D"https://en.wikipedia.org/wik=
i/Curiously_recurring_template_pattern">CRTP</a> to share implementations, =
but it's a huge pain. This requires writing a forwarding function for every=
member of the base class, which means if the base class changes, the deriv=
ed class does not get the updates. Plus, each owning class must make the te=
mplate base a friend explicitly, just to access the internal members.</p>
<p>Alleviating these problems would make inner classes even more useful, al=
lowing them to be used to implement a form of mixins and such.</p>
<p>To do that, we could define the concept of an "inner class template=
". That is, one could declare an inner class outside the scope of a cl=
ass, but only explicitly as a template. One of the template parameters woul=
d be the owning class type. And thus, name accesses that could implicitly u=
se <code>this</code> would be considered dependent accesses.</p>
<p>The use of such a template mirrors the restrictions of inner classes. Th=
ey could be instantiated essentially anywhere (but only with a complete typ=
e for the owning class), but variables of an instantiated type could only b=
e declared in the woning class (or a non-virtually derived one). Such insta=
ntiations should retain the properties of inner classes: being friends of i=
ts containing scope and so forth.</p>
<p>Then again, it would also break encapsulation. Because template inner cl=
asses are implicitly friends of their owning type, it would be possible for=
a derived class to access a base class's private data by simply creating a=
template inner class, but giving it the owning type of its base class.</p>
<div class=3D"footnotes">
<hr />
<ol>
<li id=3D"fn1"><p>The point of stateless classes is that they don't take up=
memory when they are members of objects. However, each array element has t=
o take up space; this is a basic rule of C++ and pointer arithmetic. Forbid=
ding stateless classes from being aggregated into arrays ensures that the u=
ser's expectation (not taking up space) are always met. This could be limit=
ed solely to arrays declared as NSDM's, but it's cleaner to just forbid the=
m altogether.<a href=3D"#fnref1">=E2=86=A9</a></p></li>
<li id=3D"fn2"><p>This is as much due to lifetime issues as anything else. =
Java is garbage collected, so it is reasonable to have a <code>this</code> =
pointer hidden within an inner class that points to the owning instance. An=
d thus, the owning instance will not be collected until after its inner cla=
ss objects are no longer in use. In C++, object lifetimes don't work that w=
ay, so we restrict this functionality to the one scenario where inner objec=
t lifetimes can be guaranteed. That is, direct members of the owning class.=
<a href=3D"#fnref2">=E2=86=A9</a></p></li>
<li id=3D"fn3"><p>The non-virtual part is for ease-of-implementation reason=
s. It is not yet clear if a static byte offset would be sufficient with imp=
lementations of virtual inheritance. It is also unclear if some alternative=
implementation would also be possible that could work with virtual inherit=
ance.<a href=3D"#fnref3">=E2=86=A9</a></p></li>
<li id=3D"fn4"><p>The non-virtual part is for ease-of-implementation reason=
s. It is not yet clear if a static byte offset would be sufficient with imp=
lementations of virtual inheritance. It is also unclear if some alternative=
implementation would also be possible that could work with virtual inherit=
ance.<a href=3D"#fnref4">=E2=86=A9</a></p></li>
<li id=3D"fn5"><p>The non-virtual part is for ease-of-implementation reason=
s. It is not yet clear if a static byte offset would be sufficient with imp=
lementations of virtual inheritance. It is also unclear if some alternative=
implementation would also be possible that could work with virtual inherit=
ance.<a href=3D"#fnref5">=E2=86=A9</a></p></li>
</ol>
</div>
</body>
</html>
------=_Part_2922_311081620.1441580651504--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Mon, 07 Sep 2015 10:12:57 -0400
Raw View
On 2015-09-04 16:57, Miro Knejp wrote:
> Am 04.09.2015 um 22:40 schrieb Matthew Woehlke:
>> On 2015-09-04 16:01, Miro Knejp wrote:
>>> Further, an offset member is only necessary if the nested class
>>> accesses non-static members of the parent.
>>
>> Wrong. The compiler must know the size of the class from *class*
>> definition (e.g. foo.h), which may not contain definitions of the class
>> *members* (e.g foo.cpp). Therefore, the compiler *must* assume that such
>> access occurs.
>
> Seriously? Just two lines further down I even mention that "This can
> only be determined if all member functions are defined inline, otherwise
> the compiler has to assume it needs an offset."
Okay, maybe I missed that. Apologies. Still...
>> Even if all methods are inline, the notion that changing the code in a
>> member function can change the size of a type... just... no.
I stand by this.
> Probably true. If you never access the parent you can just as well use a
> regular subobject.
Not sure how this relates?
>>> * An empty nested class is TriviallyCopyable under the same rules as if
>>> it were a regular class (assuming empty inline classes all have the same
>>> "this" pointer as the parent and thus cannot form arrays).
>>
>> An empty (inline) class (inner or not) cannot be constructed or
>> destroyed, period, and is irrelevant to whether any object having one as
>> a member is trivially copyable.
>
> Why not?
Because it occupies no storage. Hmm... the only possible use of
ctors/dtors would be for side effects. I suppose that *might* be useful,
but OTOH it's easier to simply state that such objects are irrelevant to
construction/destruction than work out the rules for the same.
Maybe that could be relaxed...
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Mon, 07 Sep 2015 13:33:14 -0400
Raw View
On 2015-09-06 19:04, Nicol Bolas wrote:
> OK, given the various discussions on this thread, I've put together an=20
> alternate look at this
Thanks! (Saved me the trouble ;-)...)
> The sizeof a stateless class is not affected by the fact that it is state=
less
Question: this would imply that, for:
struct Foo
{
<<stateless>> class { ... } bar;
int a;
};
....sizeof(Foo::bar) !=3D 0. Is that going to be a problem? (Note that e.g.
a na=C3=AFve attempt to memcpy 'bar' from one Foo to another will clobber
Foo::a as a result.) Is this just a case of "don't do that"?
> Variables which are arrays of stateless types cannot be declared.=20
> Arrays of stateless types can still be allocated with new[]. (This=20
> could be limited solely to arrays declared as NSDM's, but it's
> cleaner to just forbid them altogether.)
This seems inconsistent; why forbid automatic storage but allow dynamic
(heap) storage? Why not either allow arrays where a single instance
would also have storage (i.e. non-NSDM's), or forbid them entirely?
Oh, and this made me think of another interesting question: does a
static member of stateless type have storage? I want to say "yes" (or
possibly "implementation defined"), so that you can do things like take
its address.
> Question: Do we want stateless class NSDM's to take part in
> aggregate initialization? If we want stateless class members to
> effectively be transparent, to purely be implementation details, then
> we probably don't want them to be part of aggregate initialization.
> At the same time, stateless classes still exist and can still have
> constructors.
I could say "no, conditionally", but I think a better answer is to
provide ctors if you don't want them being mentioned in constructing the
containing class. (I.e. suppress aggregate initialization.) Ergo, my
inclination is to just say "yes".
> The inner class can access NSDM's=20
Nit: shouldn't that just be "members"? I don't think the intent was to
limit to data members? (And why limit to non-static?)
> Inner class members can be copied/moved exactly as any other type.=20
> [note: Specifically, if a stateful inner class member is trivially=20
> copyable, it should be legal to do a memcpy(dest, src, sizeof(T)) on=20
> it into another inner class member of the same type.]
(...and further discussed in Implementation.)
I think this can be broken down into three cases:
1. Trivial copyability as part of copying the containing class.
2. Trivial copyability as performed by a compiler-generated function.
3. Trivial copyability as the user might wish to manually accomplish.
For #2 and #3, I mean specifically in instances that aren't covered by
#1, i.e. copying (an) inner class member(s) outside of copying the
entire containing class. I don't see a problem with cases #1 and #2.
What's the problem with declaring #3 to be UB?
> The inner class of a class is implicitly friends
Editorial: singular/plural mismatch ;-).
> Pointers and references to inner class members can be converted to=20
> point to their owning class instance via static_cast. Such=20
> conversions can also be implicit, much like converting from a
> derived class to a base class
I'm not sure about this. I mean, I see no technical problem, but at one
point I had it in mind to forbid this for the sake of encapsulation. I
wouldn't say I feel strongly about it, however.
> If a pointer/reference to an inner class names a member of an owning clas=
s
That said, I'm rather less enamored with this. For one, it allows
writing such nonsense as:
class Outer { <<inner>> class { ... } a, b; int c; };
C c;
c.a.b.a.b.a.a.a.b.b.a.b.a.c =3D 5;
> For multiple nestings of stateful inner classes, each level has its
> own offset to get to the next level.
Um... right. I hadn't thought about that. Good catch :-).
> So forbidding arrays of inner classes of any kind is not too far afield.
It occurs to me that this would be more palatable if we had something to
declare what "looks" like an array of inner classes despite being
something different under the hood.
> The outer class cannot be trivial however, since its default
> constructor (and every other non-copy/move constructor) will need to
> initialize this hidden data.
I suggest adding to this a note that this is true for any type with
non-stateless inner classes.
Also, I think the "every other..." part is not technically true; *any*
construction needs to initialize the offsets; some may be able to do so
more trivially than others. (E.g. copy/move can just duplicate the
offset bits from the other instance, along with everything else.
Pedantically, it's still initializing them, however.)
> Trivial copyability is probably more generally useful than the
> ability to aggregate stateful inner classes into arrays.
Agreed, but both would still be better :-).
> Inner classes can be used for properties.
I, of course, would also include my "as-a interface" example here. For
that matter, since we're talking about (possibly) stateful inner
classes, the "service object" example is also worth mentioning. The more
use cases, the better :-).
I might also add: "(with zero overhead in the case of stateless inner
classes)".
> To do that, we could define the concept of an "inner class template".
> That is, one could declare an inner class outside the scope of a
> class, but only explicitly as a template. One of the template
> parameters would be the owning class type. And thus, name accesses
> that could implicitly use this would be considered dependent
> accesses.
I'll also reiterate that I think CRTP in general could use some love;
inner classes would provide increased incentive for that. I'll also go
so far as to say I see nothing in the above that appears to me to be
specific to use of CRTP with inner classes. Why limit it to inner classes?
> woning
Editorial: typo (s.b. "owning") ;-).
Thanks again!
--=20
Matthew
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Mon, 07 Sep 2015 13:40:17 -0400
Raw View
On 2015-09-05 09:23, Nicol Bolas wrote:
> On Friday, September 4, 2015 at 4:45:09 PM UTC-4, Matthew Woehlke wrote:
>> I'm still not convinced this is a(n insurmountable) problem. If it's the
>> *same* member (but of a different containing instance), then the offset
>> is the same and can be copied also, so the case of the outer class being
>> trivially copied is not broken. If it's a *different* member, you just
>> can't group doing copies of multiple consecutive inner classes into a
>> single memmove.
>
> It doesn't matter how many are being copied, one or twenty. If it can't be
> copied with a memcpy, it's not trivially copyable.
In the same member case (one or many elements), you're doing something like:
memcpy(dst, src, num_elems * sizeof(T));
In the different member, one element case, you're doing something like:
memcpy(fixup(dst), fixup(src), adjusted_size<T>());
Both cases are memcpy, though I suppose you are objecting to the need to
do some pointer/size adjustments in the second case.
Anyway, the third case I was referring to, which can never work, is:
memmove(&dst[j], &src[i], n * sizeof(T));
....that is, doing a "copy" (in this case I'm also considering the
possibility that src==dst, i.e. shuffling values within an array) of
multiple contiguous elements from one block to another, where the source
and destination blocks are different members (either entirely, or
different subsections of the same array member). That's the case that
*can't* work. The single case "can" (if you permit it to be adjusted to
exclude the offset bytes).
Hmm... it occurs to me to wonder, if we were to simply forbid arrays,
how many compilers would choose to support them anyway as an extension
and just say "don't to that" as far as the case where a user memcpy
would break things.
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Miro Knejp <miro.knejp@gmail.com>
Date: Mon, 7 Sep 2015 20:59:10 +0200
Raw View
Am 07.09.2015 um 16:12 schrieb Matthew Woehlke:
>> Probably true. If you never access the parent you can just as well use a
>> regular subobject.
> Not sure how this relates?
I agreed with your statement.
>>>> * An empty nested class is TriviallyCopyable under the same rules as if
>>>> it were a regular class (assuming empty inline classes all have the same
>>>> "this" pointer as the parent and thus cannot form arrays).
>>> An empty (inline) class (inner or not) cannot be constructed or
>>> destroyed, period, and is irrelevant to whether any object having one as
>>> a member is trivially copyable.
>> Why not?
> Because it occupies no storage. Hmm... the only possible use of
> ctors/dtors would be for side effects. I suppose that *might* be useful,
> but OTOH it's easier to simply state that such objects are irrelevant to
> construction/destruction than work out the rules for the same.
>
> Maybe that could be relaxed...
I don't see the need for special rules. The order of ctor/dtor/copy
calls should simply match the order of regular subobjects, i.e. dictated
by member declaration order. The only difference is that empty nested
classes don't occupy storage, but they can still have a well-defined
lifetime like any other subobject.
Even the array restriction may not be necessary *if* (big if) sizeof(T)
gives 0 for empty nested classes, as that doesn't violate the pointer
arithmetic of arrays. As Nicol has written in his note, there are
already cases where unrelatd empty objects can have the same address,
the real novelty is if &x[0] == &x[0] + 1 holds. This may or may not
break assumptions existing (template) code has, but if they stick to
sizeof() and type traits, generic code *shouldn't* break. But this
definitely needs more research.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Mon, 7 Sep 2015 12:04:16 -0700 (PDT)
Raw View
------=_Part_3918_2034542922.1441652656815
Content-Type: multipart/alternative;
boundary="----=_Part_3919_375320954.1441652656815"
------=_Part_3919_375320954.1441652656815
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Monday, September 7, 2015 at 1:33:35 PM UTC-4, Matthew Woehlke wrote:
>
> On 2015-09-06 19:04, Nicol Bolas wrote:=20
> > OK, given the various discussions on this thread, I've put together an=
=20
> > alternate look at this=20
>
> Thanks! (Saved me the trouble ;-)...)=20
>
> > The sizeof a stateless class is not affected by the fact that it is=20
> stateless=20
>
> Question: this would imply that, for:=20
>
> struct Foo=20
> {=20
> <<stateless>> class { ... } bar;=20
> int a;=20
> };=20
>
> ...sizeof(Foo::bar) !=3D 0. Is that going to be a problem? (Note that e.g=
..=20
> a na=C3=AFve attempt to memcpy 'bar' from one Foo to another will clobber=
=20
> Foo::a as a result.) Is this just a case of "don't do that"?
>
It would be no different than this:
class empty {};
class derived1 : public empty
{
int foo;
};
class derived2 : public empty
{
float bar;
};
You can get pointers to both empty base class objects and try to copy them=
=20
with `memcpy`. And it will fail in the exactly the same way.
Using memcpy on empty types, whether stateless or not, has problems. OK, to=
=20
be fair, it only has problems currently if the empty type is a base class=
=20
undergoing EBO.
> Variables which are arrays of stateless types cannot be declared.=20
> > Arrays of stateless types can still be allocated with new[]. (This=20
> > could be limited solely to arrays declared as NSDM's, but it's=20
> > cleaner to just forbid them altogether.)=20
>
> This seems inconsistent; why forbid automatic storage but allow dynamic=
=20
> (heap) storage? Why not either allow arrays where a single instance=20
> would also have storage (i.e. non-NSDM's), or forbid them entirely?
>
The reason to forbid variable arrays of stateless types is because=20
stateless types are not supposed to have storage. Even when not declared as=
=20
members of types (globals, automatic locals, etc), the compiler is allowed=
=20
to not give them any particular storage.
Arrays *have* to have storage, so to resolve the contradiction, we don't=20
allow stateless arrays.
*However*, if you allocate a stateless type with `new` (of any form), you=
=20
are *explicitly* asking for something which has storage. Therefore, if you=
=20
can use `new` on a stateless type, you ought to be able to use `new[]` on=
=20
it as well.
Oh, and this made me think of another interesting question: does a=20
> static member of stateless type have storage?
A static member of any type is simply a global member that has to be=20
qualified by that type's name (and for member functions, gets free access=
=20
to that type's privates). That's the only difference between a static=20
member and a global declaration.
There's no reason for the fact that the declaration is in a stateless type=
=20
(or for that matter, inner type) to affect this.
> Inner class members can be copied/moved exactly as any other type.=20
> > [note: Specifically, if a stateful inner class member is trivially=20
> > copyable, it should be legal to do a memcpy(dest, src, sizeof(T)) on=20
> > it into another inner class member of the same type.]=20
>
> (...and further discussed in Implementation.)=20
>
> I think this can be broken down into three cases:=20
>
> 1. Trivial copyability as part of copying the containing class.=20
> 2. Trivial copyability as performed by a compiler-generated function.=20
> 3. Trivial copyability as the user might wish to manually accomplish.
>
> For #2 and #3, I mean specifically in instances that aren't covered by=20
> #1, i.e. copying (an) inner class member(s) outside of copying the=20
> entire containing class. I don't see a problem with cases #1 and #2.=20
> What's the problem with declaring #3 to be UB?
>
You were the one who suggested that the user be allowed to invoke copy/move=
=20
constructors/assignment themselves when I originally suggested that the=20
user ought not be able to copy/move inner classes directly.
I see no reason to declare this to be undefined behavior. It should either=
=20
be allowed or forbidden.
=20
> > Pointers and references to inner class members can be converted to=20
> > point to their owning class instance via static_cast. Such=20
> > conversions can also be implicit, much like converting from a=20
> > derived class to a base class=20
>
> I'm not sure about this. I mean, I see no technical problem, but at one=
=20
> point I had it in mind to forbid this for the sake of encapsulation. I=20
> wouldn't say I feel strongly about it, however.
>
Orthogonality. The compiler has to be able to make that conversion anyway.=
=20
And the limits on inner classes mean that if an inner class=20
pointer/reference is valid, such conversions are reasonable (ie: their=20
owning objects also exist).
=20
> > If a pointer/reference to an inner class names a member of an owning=20
> class=20
>
> That said, I'm rather less enamored with this. For one, it allows=20
> writing such nonsense as:=20
>
> class Outer { <<inner>> class { ... } a, b; int c; };=20
> C c;=20
> c.a.b.a.b.a.a.a.b.b.a.b.a.c =3D 5;=20
>
Actually, it also means you can write `c.a.a.a.a.a`.
And even if you forbid implicit conversions outside of the implementation=
=20
of an inner class, you could still do that *within* the inner class. So=20
there's no way around that one: if an inner class can reach the outer class=
=20
members, then it can reach members of inner class type. Including members=
=20
of its own type.
> So forbidding arrays of inner classes of any kind is not too far afield.=
=20
>
> It occurs to me that this would be more palatable if we had something to=
=20
> declare what "looks" like an array of inner classes despite being=20
> something different under the hood.
>
Define "looks". Arrays have certain expected behavior. And if something is=
=20
going to "look" like an array, it had better *behave *like one. In every=20
detail.
Otherwise, it shouldn't look like an array.
=20
> > The outer class cannot be trivial however, since its default=20
> > constructor (and every other non-copy/move constructor) will need to=20
> > initialize this hidden data.=20
>
> I suggest adding to this a note that this is true for any type with=20
> non-stateless inner classes.
>
I did:
-> Objects which have stateful inner class members cannot be trivial types.
=20
> > Inner classes can be used for properties.=20
>
> I, of course, would also include my "as-a interface" example here.
But that's still just properties; you would implement the same thing in=20
C#/Python/etc as properties. It's only zero overhead because your proxy=20
objects take up no room.
For=20
> that matter, since we're talking about (possibly) stateful inner=20
> classes, the "service object" example is also worth mentioning.
I don't know what that means. Can you give a more specific example?
=20
> The more=20
> use cases, the better :-).=20
>
> I might also add: "(with zero overhead in the case of stateless inner=20
> classes)".=20
>
> > To do that, we could define the concept of an "inner class template".=
=20
> > That is, one could declare an inner class outside the scope of a=20
> > class, but only explicitly as a template. One of the template=20
> > parameters would be the owning class type. And thus, name accesses=20
> > that could implicitly use this would be considered dependent=20
> > accesses.=20
>
> I'll also reiterate that I think CRTP in general could use some love;=20
> inner classes would provide increased incentive for that.
I'll also go=20
> so far as to say I see nothing in the above that appears to me to be=20
> specific to use of CRTP with inner classes. Why limit it to inner classes=
?
>
Because the feature didn't do anything special with the CRTP. It still=20
requires you to list the owning class as a template parameter explicitly.=
=20
The totality of the feature are these:
1) Inner class templates designate one of their template arguments as being=
=20
their owning class. And therefore, names that looked up through `this` are=
=20
dependent names.
2) When you instantiate the template, the class instance is owned by that=
=20
particular template parameter.
It doesn't change how the CRTP works, nor does it make it easier to use.
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
------=_Part_3919_375320954.1441652656815
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Monday, September 7, 2015 at 1:33:35 PM UTC-4, Matthew =
Woehlke wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-l=
eft: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">On 2015-09-06 19=
:04, Nicol Bolas wrote:
<br>> OK, given the various discussions on this thread, I've put tog=
ether an=20
<br>> alternate look at this
<br>
<br>Thanks! (Saved me the trouble ;-)...)
<br>
<br>> The sizeof a stateless class is not affected by the fact that it i=
s stateless
<br>
<br>Question: this would imply that, for:
<br>
<br>=C2=A0 struct Foo
<br>=C2=A0 {
<br>=C2=A0 =C2=A0 <<stateless>> class { ... } bar;
<br>=C2=A0 =C2=A0 int a;
<br>=C2=A0 };
<br>
<br>...sizeof(Foo::bar) !=3D 0. Is that going to be a problem? (Note that e=
..g.
<br>a na=C3=AFve attempt to memcpy 'bar' from one Foo to another wi=
ll clobber
<br>Foo::a as a result.) Is this just a case of "don't do that&quo=
t;?<br></blockquote><div><br>It would be no different than this:<br><br><di=
v class=3D"prettyprint" style=3D"background-color: rgb(250, 250, 250); bord=
er-color: rgb(187, 187, 187); border-style: solid; border-width: 1px; word-=
wrap: break-word;"><code class=3D"prettyprint"><div class=3D"subprettyprint=
"><span style=3D"color: #008;" class=3D"styled-by-prettify">class</span><sp=
an style=3D"color: #000;" class=3D"styled-by-prettify"> empty </span><span =
style=3D"color: #660;" class=3D"styled-by-prettify">{};</span><span style=
=3D"color: #000;" class=3D"styled-by-prettify"><br></span><span style=3D"co=
lor: #008;" class=3D"styled-by-prettify">class</span><span style=3D"color: =
#000;" class=3D"styled-by-prettify"> derived1 </span><span style=3D"color: =
#660;" class=3D"styled-by-prettify">:</span><span style=3D"color: #000;" cl=
ass=3D"styled-by-prettify"> </span><span style=3D"color: #008;" class=3D"st=
yled-by-prettify">public</span><span style=3D"color: #000;" class=3D"styled=
-by-prettify"> empty<br></span><span style=3D"color: #660;" class=3D"styled=
-by-prettify">{</span><span style=3D"color: #000;" class=3D"styled-by-prett=
ify"><br>=C2=A0 </span><span style=3D"color: #008;" class=3D"styled-by-pret=
tify">int</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> =
foo</span><span style=3D"color: #660;" class=3D"styled-by-prettify">;</span=
><span style=3D"color: #000;" class=3D"styled-by-prettify"><br></span><span=
style=3D"color: #660;" class=3D"styled-by-prettify">};</span><span style=
=3D"color: #000;" class=3D"styled-by-prettify"><br><br></span><span style=
=3D"color: #008;" class=3D"styled-by-prettify">class</span><span style=3D"c=
olor: #000;" class=3D"styled-by-prettify"> derived2 </span><span style=3D"c=
olor: #660;" class=3D"styled-by-prettify">:</span><span style=3D"color: #00=
0;" class=3D"styled-by-prettify"> </span><span style=3D"color: #008;" class=
=3D"styled-by-prettify">public</span><span style=3D"color: #000;" class=3D"=
styled-by-prettify"> empty<br></span><span style=3D"color: #660;" class=3D"=
styled-by-prettify">{</span><span style=3D"color: #000;" class=3D"styled-by=
-prettify"><br>=C2=A0 </span><span style=3D"color: #008;" class=3D"styled-b=
y-prettify">float</span><span style=3D"color: #000;" class=3D"styled-by-pre=
ttify"> bar</span><span style=3D"color: #660;" class=3D"styled-by-prettify"=
>;</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br></sp=
an><span style=3D"color: #660;" class=3D"styled-by-prettify">};</span></div=
></code></div><br>You can get pointers to both empty base class objects and=
try to copy them with `memcpy`. And it will fail in the exactly the same w=
ay.<br><br>Using memcpy on empty types, whether stateless or not, has probl=
ems. OK, to be fair, it only has problems currently if the empty type is a =
base class undergoing EBO.<br><br></div><blockquote class=3D"gmail_quote" s=
tyle=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-le=
ft: 1ex;">
> Variables which are arrays of stateless types cannot be declared.=20
<br>> Arrays of stateless types can still be allocated with new[]. (This=
=20
<br>> could be limited solely to arrays declared as NSDM's, but it&#=
39;s
<br>> cleaner to just forbid them altogether.)
<br>
<br>This seems inconsistent; why forbid automatic storage but allow dynamic
<br>(heap) storage? Why not either allow arrays where a single instance
<br>would also have storage (i.e. non-NSDM's), or forbid them entirely?=
<br></blockquote><div><br>The reason to forbid variable arrays of stateless=
types is because stateless types are not supposed to have storage. Even wh=
en not declared as members of types (globals, automatic locals, etc), the c=
ompiler is allowed to not give them any particular storage.<br><br>Arrays <=
i>have</i> to have storage, so to resolve the contradiction, we don't a=
llow stateless arrays.<br><br><i>However</i>, if you allocate a stateless t=
ype with `new` (of any form), you are <i>explicitly</i> asking for somethin=
g which has storage. Therefore, if you can use `new` on a stateless type, y=
ou ought to be able to use `new[]` on it as well.<br><br></div><blockquote =
class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1p=
x #ccc solid;padding-left: 1ex;">
Oh, and this made me think of another interesting question: does a
<br>static member of stateless type have storage?</blockquote><div><br>A st=
atic member of any type is simply a global member that has to be qualified =
by that type's name (and for member functions, gets free access to that=
type's privates). That's the only difference between a static memb=
er and a global declaration.<br><br>There's no reason for the fact that=
the declaration is in a stateless type (or for that matter, inner type) to=
affect this.<br><br></div><blockquote class=3D"gmail_quote" style=3D"margi=
n: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">>=
; Inner class members can be copied/moved exactly as any other type.=20
<br>> [note: Specifically, if a stateful inner class member is trivially=
=20
<br>> copyable, it should be legal to do a memcpy(dest, src, sizeof(T)) =
on=20
<br>> it into another inner class member of the same type.]
<br>
<br>(...and further discussed in Implementation.)
<br>
<br>I think this can be broken down into three cases:
<br>
<br>1. Trivial copyability as part of copying the containing class.
<br>2. Trivial copyability as performed by a compiler-generated function.
<br>3. Trivial copyability as the user might wish to manually accomplish.<b=
r></blockquote><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-=
left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
<br>For #2 and #3, I mean specifically in instances that aren't covered=
by
<br>#1, i.e. copying (an) inner class member(s) outside of copying the
<br>entire containing class. I don't see a problem with cases #1 and #2=
..
<br>What's the problem with declaring #3 to be UB?<br></blockquote><div=
><br>You were the one who suggested that the user be allowed to invoke copy=
/move constructors/assignment themselves when I originally suggested that t=
he user ought not be able to copy/move inner classes directly.<br><br>I see=
no reason to declare this to be undefined behavior. It should either be al=
lowed or forbidden.<br>=C2=A0</div><blockquote class=3D"gmail_quote" style=
=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: =
1ex;">
> Pointers and references to inner class members can be converted to=20
<br>> point to their owning class instance via static_cast. Such=20
<br>> conversions can also be implicit, much like converting from a
<br>> derived class to a base class
<br>
<br>I'm not sure about this. I mean, I see no technical problem, but at=
one
<br>point I had it in mind to forbid this for the sake of encapsulation. I
<br>wouldn't say I feel strongly about it, however.<br></blockquote><di=
v><br>Orthogonality. The compiler has to be able to make that conversion an=
yway. And the limits on inner classes mean that if an inner class pointer/r=
eference is valid, such conversions are reasonable (ie: their owning object=
s also exist).<br>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"ma=
rgin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
> If a pointer/reference to an inner class names a member of an owning c=
lass
<br>
<br>That said, I'm rather less enamored with this. For one, it allows
<br>writing such nonsense as:
<br>
<br>=C2=A0 class Outer { <<inner>> class { ... } a, b; int c; }=
;
<br>=C2=A0 C c;
<br>=C2=A0 c.a.b.a.b.a.a.a.b.b.a.b.a.c =3D 5;
<br></blockquote><div><br>Actually, it also means you can write `c.a.a.a.a.=
a`.<br><br>And even if you forbid implicit conversions outside of the imple=
mentation of an inner class, you could still do that <i>within</i> the inne=
r class. So there's no way around that one: if an inner class can reach=
the outer class members, then it can reach members of inner class type. In=
cluding members of its own type.<br><br></div><blockquote class=3D"gmail_qu=
ote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padd=
ing-left: 1ex;">
> So forbidding arrays of inner classes of any kind is not too far afiel=
d.
<br>
<br>It occurs to me that this would be more palatable if we had something t=
o
<br>declare what "looks" like an array of inner classes despite b=
eing
<br>something different under the hood.<br></blockquote><div><br>Define &qu=
ot;looks". Arrays have certain expected behavior. And if something is =
going to "look" like an array, it had better <i>behave </i>like o=
ne. In every detail.<br><br>Otherwise, it shouldn't look like an array.=
<br>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin=
-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
> The outer class cannot be trivial however, since its default
<br>> constructor (and every other non-copy/move constructor) will need =
to
<br>> initialize this hidden data.
<br>
<br>I suggest adding to this a note that this is true for any type with
<br>non-stateless inner classes.<br></blockquote><div><br>I did:<br><br>-&g=
t; Objects which have stateful inner class members cannot be trivial types.=
<br>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin=
-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
> Inner classes can be used for properties.
<br>
<br>I, of course, would also include my "as-a interface" example =
here.</blockquote><div><br>But that's still just properties; you would =
implement the same thing in C#/Python/etc as properties. It's only zero=
overhead because your proxy objects take up no room.<br><br></div><blockqu=
ote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left=
: 1px #ccc solid;padding-left: 1ex;"> For
<br>that matter, since we're talking about (possibly) stateful inner
<br>classes, the "service object" example is also worth mentionin=
g.</blockquote><div><br>I don't know what that means. Can you give a mo=
re specific example?<br>=C2=A0</div><blockquote class=3D"gmail_quote" style=
=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: =
1ex;"> The more
<br>use cases, the better :-).
<br>
<br>I might also add: "(with zero overhead in the case of stateless in=
ner
<br>classes)".
<br>
<br>> To do that, we could define the concept of an "inner class te=
mplate".
<br>> That is, one could declare an inner class outside the scope of a
<br>> class, but only explicitly as a template. One of the template
<br>> parameters would be the owning class type. And thus, name accesses
<br>> that could implicitly use this would be considered dependent
<br>> accesses.
<br>
<br>I'll also reiterate that I think CRTP in general could use some lov=
e;
<br>inner classes would provide increased incentive for that.</blockquote><=
blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bord=
er-left: 1px #ccc solid;padding-left: 1ex;">I'll also go
<br>so far as to say I see nothing in the above that appears to me to be
<br>specific to use of CRTP with inner classes. Why limit it to inner class=
es?<br></blockquote><div><br>Because the feature didn't do anything spe=
cial with the CRTP. It still requires you to list the owning class as a tem=
plate parameter explicitly. The totality of the feature are these:<br><br>1=
) Inner class templates designate one of their template arguments as being =
their owning class. And therefore, names that looked up through `this` are =
dependent names.<br><br>2) When you instantiate the template, the class ins=
tance is owned by that particular template parameter.<br><br>It doesn't=
change how the CRTP works, nor does it make it easier to use.<br></div></d=
iv>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_3919_375320954.1441652656815--
------=_Part_3918_2034542922.1441652656815--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Mon, 07 Sep 2015 16:08:32 -0400
Raw View
On 2015-09-07 15:04, Nicol Bolas wrote:
> On Monday, September 7, 2015 at 1:33:35 PM UTC-4, Matthew Woehlke wrote:
>> On 2015-09-06 19:04, Nicol Bolas wrote:=20
>>> The sizeof a stateless class is not affected by the fact that it is=20
>>> stateless=20
>>
>> Question: this would imply that, for:=20
>>
>> struct Foo=20
>> {=20
>> <<stateless>> class { ... } bar;=20
>> int a;=20
>> };=20
>>
>> ...sizeof(Foo::bar) !=3D 0. Is that going to be a problem? (Note that e.=
g.=20
>> a na=C3=AFve attempt to memcpy 'bar' from one Foo to another will clobbe=
r=20
>> Foo::a as a result.) Is this just a case of "don't do that"?
>=20
> It would be no different than [example elided]
>=20
> Using memcpy on empty types, whether stateless or not, has problems. OK, =
to=20
> be fair, it only has problems currently if the empty type is a base class=
=20
> undergoing EBO.
Okay. Sounds like we agree it isn't a problem. I sort-of expected that,
but just wanted to point it out.
>>> Variables which are arrays of stateless types cannot be declared.=20
>>> Arrays of stateless types can still be allocated with new[]. (This=20
>>> could be limited solely to arrays declared as NSDM's, but it's=20
>>> cleaner to just forbid them altogether.)=20
>>
>> This seems inconsistent; why forbid automatic storage but allow dynamic=
=20
>> (heap) storage? Why not either allow arrays where a single instance=20
>> would also have storage (i.e. non-NSDM's), or forbid them entirely?
>=20
> The reason to forbid variable arrays of stateless types is because=20
> stateless types are not supposed to have storage. Even when not declared =
as=20
> members of types (globals, automatic locals, etc), the compiler is allowe=
d=20
> to not give them any particular storage.
So... related to:
>> Oh, and this made me think of another interesting question: does a=20
>> static member of stateless type have storage?
....what happens then when I take the address of such a critter? Is it
allowed to be null? (I take it this means that a na=C3=AFve memcpy of a
stateless type is UB?)
I was assuming that a non-NSDM stateless type would simply have minimal
real (if pointless) storage, since it's likely not going to be a
performance issue for non-NSDM's (as there will necessarily be O(1) of
them) and it eliminates bunches of questions like the above. In which
case, the array prohibition seems silly.
If that's not the case, however, then it makes more sense.
>> Inner class members can be copied/moved exactly as any other type.=20
>>> [note: Specifically, if a stateful inner class member is trivially=20
>>> copyable, it should be legal to do a memcpy(dest, src, sizeof(T)) on=20
>>> it into another inner class member of the same type.]=20
>>
>> (...and further discussed in Implementation.)=20
>>
>> I think this can be broken down into three cases:=20
>>
>> 1. Trivial copyability as part of copying the containing class.=20
>> 2. Trivial copyability as performed by a compiler-generated function.=20
>> 3. Trivial copyability as the user might wish to manually accomplish.
>>
>=20
>> For #2 and #3, I mean specifically in instances that aren't covered by=
=20
>> #1, i.e. copying (an) inner class member(s) outside of copying the=20
>> entire containing class. I don't see a problem with cases #1 and #2.=20
>> What's the problem with declaring #3 to be UB?
>=20
> You were the one who suggested that the user be allowed to invoke copy/mo=
ve=20
> constructors/assignment themselves when I originally suggested that the=
=20
> user ought not be able to copy/move inner classes directly.
....because I don't see how you're supposed to write an outer class user
defined copy ctor if you can't call the inner class members' copy ctors.
> I see no reason to declare this to be undefined behavior. It should eithe=
r=20
> be allowed or forbidden.
I'm not talking about the *copy ctor*; the compiler can easily generate
that to copy only the non-offset bits. I'm talking about *user code*
doing a memcpy/memmove. The suggestion would be that for the *user* to
call memcpy/memmove on an inner class's bytes is UB. (Presumably we
would then provide a way to do the necessary adjustments, but discussing
that is moot unless we agree to the UB in the first place.)
>>> Pointers and references to inner class members can be converted to=20
>>> point to their owning class instance via static_cast. Such=20
>>> conversions can also be implicit, much like converting from a=20
>>> derived class to a base class=20
>>
>> I'm not sure about this. I mean, I see no technical problem, but at one=
=20
>> point I had it in mind to forbid this for the sake of encapsulation. I=
=20
>> wouldn't say I feel strongly about it, however.
>=20
> Orthogonality. The compiler has to be able to make that conversion anyway=
..=20
> And the limits on inner classes mean that if an inner class=20
> pointer/reference is valid, such conversions are reasonable (ie: their=20
> owning objects also exist).
True, but then, the compiler can cast from a derived class to a private
base. As I said, the reason to deny it is for encapsulation, i.e.
reasons similar to disallowing outside access to a private member.
Keep in mind, we can always forbid it now and change our minds later. We
can't go the other way.
>>> If a pointer/reference to an inner class names a member of an owning=20
>> class=20
>>
>> That said, I'm rather less enamored with this. For one, it allows=20
>> writing such nonsense as:=20
>>
>> class Outer { <<inner>> class { ... } a, b; int c; };=20
>> C c;=20
>> c.a.b.a.b.a.a.a.b.b.a.b.a.c =3D 5;=20
>=20
> Actually, it also means you can write `c.a.a.a.a.a`.
(You'll notice I wasn't strictly alternating in that example ;-).)
> And even if you forbid implicit conversions outside of the implementation=
=20
> of an inner class, you could still do that *within* the inner class.
Yes and no. You couldn't 'this->a.b' if decltype(*this) !=3D decltype(a),
because 'b' would be a protected member in that case. (At least it was;
my original suggestion was for access to the outer class to be
protected.) For that matter, we could allow outer member access only via
'this', which would prevent anything but one layer of self recursion
(i.e. 'this->a.b' where 'this' =3D=3D '&this->a').
>>> So forbidding arrays of inner classes of any kind is not too far afield=
..=20
>>
>> It occurs to me that this would be more palatable if we had something to=
=20
>> declare what "looks" like an array of inner classes despite being=20
>> something different under the hood.
>=20
> Define "looks".
Has an operator[](size_t). In fact, that's pretty much the only
requirement I'd place on it.
For example:
class Outer
{
private: <<inner>> class P { ... } _0, _1, _2, _3;
public: <<stateless>> <<inner>> class {
P& operator[](size_t) { ... }
} p;
};
(I was going to suggest something that would hold an array of
references, but I forgot those aren't legal ;-).)
Certainly you can hack something up by hand. The "something" mentioned
above would likely work similarly, but would move the boilerplate into a
library class. (Ideally it would be usable like std::array, i.e. a
template taking a typename and a count, that creates the actual members
internally rather than referring to external members.)
> Arrays have certain expected behavior. And if something is=20
> going to "look" like an array, it had better *behave *like one. In every=
=20
> detail.
>=20
> Otherwise, it shouldn't look like an array.
Probably you would not be able to do things like pass such an array as a
parameter (unless by reference to the aforementioned utility type).
Certainly you wouldn't have direct access to the block of bytes.
>>> The outer class cannot be trivial however, since its default=20
>>> constructor (and every other non-copy/move constructor) will need to=20
>>> initialize this hidden data.=20
>>
>> I suggest adding to this a note that this is true for any type with=20
>> non-stateless inner classes.
>=20
> I did:
>=20
> -> Objects which have stateful inner class members cannot be trivial type=
s.
Key phrase: "to this"; apparently I'd forgotten that by the time I got
to the above quoted text. (Actually... I'm not entirely sure what I was
thinking when I wrote the above...)
>>> Inner classes can be used for properties.=20
>>
>> I, of course, would also include my "as-a interface" example here.
>=20
> But that's still just properties; you would implement the same thing in=
=20
> C#/Python/etc as properties. It's only zero overhead because your proxy=
=20
> objects take up no room.
Not exactly; in that case, you probably don't have assignment /
conversion (or if you do, they look like modifying the whole class), and
they almost *certainly* have other members, which properties usually
would not.
Anyway, it's not important; at most it strengthens the rationale.
>> For that matter, since we're talking about (possibly) stateful
>> inner classes, the "service object" example is also worth
>> mentioning.
>=20
> I don't know what that means. Can you give a more specific example?
See Edward Catmur's reply=C2=B9 to your request for use cases in the previo=
us
thread. As I understand, this would be useful when you have several
"iterations" of essentially the same functionality, but with different
purposes. For example, a stream writer that needs to access the parent
class, which might be instantiated for stdout and stderr, and used for
different purposes (e.g. normal operation, errors).
(=C2=B9 http://permalink.gmane.org/gmane.comp.lang.c++.isocpp.proposals/201=
42)
>>> To do that, we could define the concept of an "inner class template".=
=20
>>> That is, one could declare an inner class outside the scope of a=20
>>> class, but only explicitly as a template. One of the template=20
>>> parameters would be the owning class type. And thus, name accesses=20
>>> that could implicitly use this would be considered dependent=20
>>> accesses.=20
>>
>> I'll also reiterate that I think CRTP in general could use some love;=20
>> inner classes would provide increased incentive for that.
>>=20
>> I'll also go so far as to say I see nothing in the above that
>> appears to me to be specific to use of CRTP with inner classes. Why
>> limit it to inner classes?
>=20
> Because the feature didn't do anything special with the CRTP. It still=20
> requires you to list the owning class as a template parameter explicitly.=
=20
> The totality of the feature are these:
>=20
> 1) Inner class templates designate one of their template arguments as bei=
ng=20
> their owning class. And therefore, names that looked up through `this` ar=
e=20
> dependent names.
This sounds a lot like CRTP, or at least one of the sorts of problems
that CRTP addresses.
> 2) When you instantiate the template, the class instance is owned by that=
=20
> particular template parameter.
Presumably this means new syntax.
Why not this instead:
A mixin=C2=B2 template is a class template that must be subclassed in order
to be used. All references to 'this' are deferred until the template is
actually instantiated. When you instantiate the mixin template, the
actual type of *the template* is implicitly converted to the derived
type, with 'this' names resolved accordingly.
Does that not solve the problem equally well? I think yes, *but* it is
useful to non-inner classes as well, which makes it far more broadly
useful (likely able to replace many existing uses of CRTP).
I don't object to making CRTP / CRTP-like things easier. I *do* object
to doing so for only a limited subset of use cases for no obvious reason.
(=C2=B2 Name subject to change; mainly, I'm calling it something other than
CRTP because derivation would not need to repeat the class name.)
--=20
Matthew
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Wed, 9 Sep 2015 12:50:15 -0700 (PDT)
Raw View
------=_Part_981_1733026938.1441828215891
Content-Type: multipart/alternative;
boundary="----=_Part_982_1627149755.1441828215892"
------=_Part_982_1627149755.1441828215892
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Monday, September 7, 2015 at 4:08:58 PM UTC-4, Matthew Woehlke wrote:
>
> On 2015-09-07 15:04, Nicol Bolas wrote:=20
> > On Monday, September 7, 2015 at 1:33:35 PM UTC-4, Matthew Woehlke wrote=
:=20
> >> On 2015-09-06 19:04, Nicol Bolas wrote:=20
> >>> The sizeof a stateless class is not affected by the fact that it is=
=20
> >>> stateless=20
> >>=20
> >> Question: this would imply that, for:=20
> >>=20
> >> struct Foo=20
> >> {=20
> >> <<stateless>> class { ... } bar;=20
> >> int a;=20
> >> };=20
> >>=20
> >> ...sizeof(Foo::bar) !=3D 0. Is that going to be a problem? (Note that=
=20
> e.g.=20
> >> a na=C3=AFve attempt to memcpy 'bar' from one Foo to another will clob=
ber=20
> >> Foo::a as a result.) Is this just a case of "don't do that"?=20
> >=20
> > It would be no different than [example elided]=20
> >=20
> > Using memcpy on empty types, whether stateless or not, has problems. OK=
,=20
> to=20
> > be fair, it only has problems currently if the empty type is a base=20
> class=20
> > undergoing EBO.=20
>
> Okay. Sounds like we agree it isn't a problem. I sort-of expected that,=
=20
> but just wanted to point it out.=20
>
> >>> Variables which are arrays of stateless types cannot be declared.=20
> >>> Arrays of stateless types can still be allocated with new[]. (This=20
> >>> could be limited solely to arrays declared as NSDM's, but it's=20
> >>> cleaner to just forbid them altogether.)=20
> >>=20
> >> This seems inconsistent; why forbid automatic storage but allow dynami=
c=20
> >> (heap) storage? Why not either allow arrays where a single instance=20
> >> would also have storage (i.e. non-NSDM's), or forbid them entirely?=20
> >=20
> > The reason to forbid variable arrays of stateless types is because=20
> > stateless types are not supposed to have storage. Even when not declare=
d=20
> as=20
> > members of types (globals, automatic locals, etc), the compiler is=20
> allowed=20
> > to not give them any particular storage.=20
>
> So... related to:=20
>
> >> Oh, and this made me think of another interesting question: does a=20
> >> static member of stateless type have storage?=20
>
> ...what happens then when I take the address of such a critter? Is it=20
> allowed to be null?
The same thing that happens when you take the address of any stateless=20
object, whether a member or not.
You get a pointer of the stateless type, who's memory address may be shared=
=20
with other objects. For a stateless NSDM, it may have the value of its=20
owner's `this`. But it may also point to `this` + 1 byte. Or to something=
=20
else entirely.
It's up to the implementation. If an implementation wants to allow=20
stateless non-NSDMs to take up no room, that's great; they have the freedom=
=20
to do that. If they can't or don't want to, then that's fine too; give them=
=20
the minimum size.
=20
> > I see no reason to declare this to be undefined behavior. It should=20
> either=20
> > be allowed or forbidden.=20
>
> I'm not talking about the *copy ctor*; the compiler can easily generate=
=20
> that to copy only the non-offset bits. I'm talking about *user code*=20
> doing a memcpy/memmove. The suggestion would be that for the *user* to=20
> call memcpy/memmove on an inner class's bytes is UB.
There's a term for types where memcpy results in UB: *not trivially=20
copyable*.
A type cannot be both trivially copyable and have memcpy yield UB. So=20
either stateful inner class members are trivially copyable (and thus memcpy=
=20
is defined behavior) or they are not (and thus memcpy is undefined).
>>> Pointers and references to inner class members can be converted to=20
> >>> point to their owning class instance via static_cast. Such=20
> >>> conversions can also be implicit, much like converting from a=20
> >>> derived class to a base class=20
> >>=20
> >> I'm not sure about this. I mean, I see no technical problem, but at on=
e=20
> >> point I had it in mind to forbid this for the sake of encapsulation. I=
=20
> >> wouldn't say I feel strongly about it, however.=20
> >=20
> > Orthogonality. The compiler has to be able to make that conversion=20
> anyway.=20
> > And the limits on inner classes mean that if an inner class=20
> > pointer/reference is valid, such conversions are reasonable (ie: their=
=20
> > owning objects also exist).=20
>
> True, but then, the compiler can cast from a derived class to a private=
=20
> base. As I said, the reason to deny it is for encapsulation, i.e.=20
> reasons similar to disallowing outside access to a private member.=20
>
> Keep in mind, we can always forbid it now and change our minds later. We=
=20
> can't go the other way.=20
>
=20
> For that matter, we could allow outer member access only via=20
> 'this', which would prevent anything but one layer of self recursion=20
> (i.e. 'this->a.b' where 'this' =3D=3D '&this->a').
>
What exactly would that mean? Are you saying that if you're an inner class,=
=20
you can refer to any member of your owning class(es)... except other inner=
=20
class members?
That's a *really* specific rule. I'm not sure why we need something like=20
that.
=20
> >>> So forbidding arrays of inner classes of any kind is not too far=20
> afield.=20
> >>=20
> >> It occurs to me that this would be more palatable if we had something=
=20
> to=20
> >> declare what "looks" like an array of inner classes despite being=20
> >> something different under the hood.=20
> >=20
> > Define "looks".=20
>
> Has an operator[](size_t). In fact, that's pretty much the only=20
> requirement I'd place on it.
>
Then it's not an array. And if you declare one in the exact same way as a=
=20
language array, yet defy the rules of language arrays, then you'll confuse=
=20
people needlessly.
>> For that matter, since we're talking about (possibly) stateful=20
> >> inner classes, the "service object" example is also worth=20
> >> mentioning.=20
> >=20
> > I don't know what that means. Can you give a more specific example?=20
>
> See Edward Catmur's reply=C2=B9 to your request for use cases in the prev=
ious=20
> thread. As I understand, this would be useful when you have several=20
> "iterations" of essentially the same functionality, but with different=20
> purposes. For example, a stream writer that needs to access the parent=20
> class, which might be instantiated for stdout and stderr, and used for=20
> different purposes (e.g. normal operation, errors).=20
>
> (=C2=B9 http://permalink.gmane.org/gmane.comp.lang.c++.isocpp.proposals/2=
0142)
>
You've done more to explain what he meant by "service object" than he did,=
=20
and it's still not clear what that code would look like. That's why actual=
=20
example code is a better example; I'm still not sure what you stream writer=
=20
suggestion would look like.
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
------=_Part_982_1627149755.1441828215892
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Monday, September 7, 2015 at 4:08:58 PM UTC-4, Matthew Woehlke wrote:<bl=
ockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border=
-left: 1px #ccc solid;padding-left: 1ex;">On 2015-09-07 15:04, Nicol Bolas =
wrote:
<br>> On Monday, September 7, 2015 at 1:33:35 PM UTC-4, Matthew Woehlke =
wrote:
<br>>> On 2015-09-06 19:04, Nicol Bolas wrote:=20
<br>>>> The sizeof a stateless class is not affected by the fact t=
hat it is=20
<br>>>> stateless=20
<br>>>
<br>>> Question: this would imply that, for:=20
<br>>>
<br>>> =C2=A0 struct Foo=20
<br>>> =C2=A0 {=20
<br>>> =C2=A0 =C2=A0 <<stateless>> class { ... } bar;=20
<br>>> =C2=A0 =C2=A0 int a;=20
<br>>> =C2=A0 };=20
<br>>>
<br>>> ...sizeof(Foo::bar) !=3D 0. Is that going to be a problem? (No=
te that e.g.=20
<br>>> a na=C3=AFve attempt to memcpy 'bar' from one Foo to a=
nother will clobber=20
<br>>> Foo::a as a result.) Is this just a case of "don't do=
that"?
<br>>=20
<br>> It would be no different than [example elided]
<br>>=20
<br>> Using memcpy on empty types, whether stateless or not, has problem=
s. OK, to=20
<br>> be fair, it only has problems currently if the empty type is a bas=
e class=20
<br>> undergoing EBO.
<br>
<br>Okay. Sounds like we agree it isn't a problem. I sort-of expected t=
hat,
<br>but just wanted to point it out.
<br>
<br>>>> Variables which are arrays of stateless types cannot be de=
clared.=20
<br>>>> Arrays of stateless types can still be allocated with new[=
]. (This=20
<br>>>> could be limited solely to arrays declared as NSDM's, =
but it's=20
<br>>>> cleaner to just forbid them altogether.)=20
<br>>>
<br>>> This seems inconsistent; why forbid automatic storage but allo=
w dynamic=20
<br>>> (heap) storage? Why not either allow arrays where a single ins=
tance=20
<br>>> would also have storage (i.e. non-NSDM's), or forbid them =
entirely?
<br>>=20
<br>> The reason to forbid variable arrays of stateless types is because=
=20
<br>> stateless types are not supposed to have storage. Even when not de=
clared as=20
<br>> members of types (globals, automatic locals, etc), the compiler is=
allowed=20
<br>> to not give them any particular storage.
<br>
<br>So... related to:
<br>
<br>>> Oh, and this made me think of another interesting question: do=
es a=20
<br>>> static member of stateless type have storage?
<br>
<br>...what happens then when I take the address of such a critter? Is it
<br>allowed to be null?</blockquote><div><br>The same thing that happens wh=
en you take the address of any stateless object, whether a member or not.<b=
r><br>You get a pointer of the stateless type, who's memory address may=
be shared with other objects. For a stateless NSDM, it may have the value =
of its owner's `this`. But it may also point to `this` + 1 byte. Or to =
something else entirely.<br><br>It's up to the implementation. If an im=
plementation wants to allow stateless non-NSDMs to take up no room, that=
9;s great; they have the freedom to do that. If they can't or don't=
want to, then that's fine too; give them the minimum size.<br>=C2=A0</=
div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex=
;border-left: 1px #ccc solid;padding-left: 1ex;">> I see no reason to de=
clare this to be undefined behavior. It should either=20
<br>> be allowed or forbidden.
<br>
<br>I'm not talking about the *copy ctor*; the compiler can easily gene=
rate
<br>that to copy only the non-offset bits. I'm talking about *user code=
*
<br>doing a memcpy/memmove. The suggestion would be that for the *user* to
<br>call memcpy/memmove on an inner class's bytes is UB.</blockquote><d=
iv><br>There's a term for types where memcpy results in UB: <u>not triv=
ially copyable</u>.<br><br>A type cannot be both trivially copyable and hav=
e memcpy yield UB. So either stateful inner class members are trivially cop=
yable (and thus memcpy is defined behavior) or they are not (and thus memcp=
y is undefined).<br><br></div><blockquote class=3D"gmail_quote" style=3D"ma=
rgin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">=
>>> Pointers and references to inner class members can be converte=
d to=20
<br>>>> point to their owning class instance via static_cast. Such=
=20
<br>>>> conversions can also be implicit, much like converting fro=
m a=20
<br>>>> derived class to a base class=20
<br>>>
<br>>> I'm not sure about this. I mean, I see no technical proble=
m, but at one=20
<br>>> point I had it in mind to forbid this for the sake of encapsul=
ation. I=20
<br>>> wouldn't say I feel strongly about it, however.
<br>>=20
<br>> Orthogonality. The compiler has to be able to make that conversion=
anyway.=20
<br>> And the limits on inner classes mean that if an inner class=20
<br>> pointer/reference is valid, such conversions are reasonable (ie: t=
heir=20
<br>> owning objects also exist).
<br>
<br>True, but then, the compiler can cast from a derived class to a private
<br>base. As I said, the reason to deny it is for encapsulation, i.e.
<br>reasons similar to disallowing outside access to a private member.
<br>
<br>Keep in mind, we can always forbid it now and change our minds later. W=
e
<br>can't go the other way.
<br></blockquote><div><br>=C2=A0</div><blockquote class=3D"gmail_quote" sty=
le=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left=
: 1ex;">For that matter, we could allow outer member access only via
<br>'this', which would prevent anything but one layer of self recu=
rsion
<br>(i.e. 'this->a.b' where 'this' =3D=3D '&this=
->a').<br></blockquote><div><br>What exactly would that mean? Are yo=
u saying that if you're an inner class, you can refer to any member of =
your owning class(es)... except other inner class members?<br><br>That'=
s a <i>really</i> specific rule. I'm not sure why we need something lik=
e that.<br>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0=
;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
>>> So forbidding arrays of inner classes of any kind is not too f=
ar afield.=20
<br>>>
<br>>> It occurs to me that this would be more palatable if we had so=
mething to=20
<br>>> declare what "looks" like an array of inner classes =
despite being=20
<br>>> something different under the hood.
<br>>=20
<br>> Define "looks".
<br>
<br>Has an operator[](size_t). In fact, that's pretty much the only
<br>requirement I'd place on it.<br></blockquote><div><br>Then it's=
not an array. And if you declare one in the exact same way as a language a=
rray, yet defy the rules of language arrays, then you'll confuse people=
needlessly.<br><br></div><blockquote class=3D"gmail_quote" style=3D"margin=
: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
>> For that matter, since we're talking about (possibly) stateful
<br>>> inner classes, the "service object" example is also =
worth
<br>>> mentioning.
<br>>=20
<br>> I don't know what that means. Can you give a more specific exa=
mple?
<br>
<br>See Edward Catmur's reply=C2=B9 to your request for use cases in th=
e previous
<br>thread. As I understand, this would be useful when you have several
<br>"iterations" of essentially the same functionality, but with =
different
<br>purposes. For example, a stream writer that needs to access the parent
<br>class, which might be instantiated for stdout and stderr, and used for
<br>different purposes (e.g. normal operation, errors).
<br>
<br>(=C2=B9 <a href=3D"http://permalink.gmane.org/gmane.comp.lang.c++.isocp=
p.proposals/20142" target=3D"_blank" rel=3D"nofollow" onmousedown=3D"this.h=
ref=3D'http://www.google.com/url?q\75http%3A%2F%2Fpermalink.gmane.org%2=
Fgmane.comp.lang.c%2B%2B.isocpp.proposals%2F20142\46sa\75D\46sntz\0751\46us=
g\75AFQjCNGgceIisDq-l8G3VlsYi8hxhsy3og';return true;" onclick=3D"this.h=
ref=3D'http://www.google.com/url?q\75http%3A%2F%2Fpermalink.gmane.org%2=
Fgmane.comp.lang.c%2B%2B.isocpp.proposals%2F20142\46sa\75D\46sntz\0751\46us=
g\75AFQjCNGgceIisDq-l8G3VlsYi8hxhsy3og';return true;">http://permalink.=
gmane.org/<wbr>gmane.comp.lang.c++.isocpp.<wbr>proposals/20142</a>)<br></bl=
ockquote><div><br>You've done more to explain what he meant by "se=
rvice object" than he did, and it's still not clear what that code=
would look like. That's why actual example code is a better example; I=
'm still not sure what you stream writer suggestion would look like.<br=
></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_982_1627149755.1441828215892--
------=_Part_981_1733026938.1441828215891--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Wed, 09 Sep 2015 17:57:57 -0400
Raw View
On 2015-09-09 15:50, Nicol Bolas wrote:
> On Monday, September 7, 2015 at 4:08:58 PM UTC-4, Matthew Woehlke wrote:
>> Oh, and this made me think of another interesting question: does a
>> static member of stateless type have storage? What happens then
>> when I take the address of such a critter? Is itallowed to be
>> null?
>
> The same thing that happens when you take the address of any stateless
> object, whether a member or not.
>
> You get a pointer of the stateless type, who's memory address may be shared
> with other objects. For a stateless NSDM, it may have the value of its
> owner's `this`. But it may also point to `this` + 1 byte. Or to something
> else entirely.
Okay, let me rephrase that; is reading data via that pointer UB? If yes,
then I guess we have no problem here.
>> On 2015-09-07 15:04, Nicol Bolas wrote:
>>> I see no reason to declare this to be undefined behavior. It should
>>> either be allowed or forbidden.
>>
>> I'm not talking about the *copy ctor*; the compiler can easily generate
>> that to copy only the non-offset bits. I'm talking about *user code*
>> doing a memcpy/memmove. The suggestion would be that for the *user* to
>> call memcpy/memmove on an inner class's bytes is UB.
>
> There's a term for types where memcpy results in UB: *not trivially
> copyable*.
Okay... where is that specified? And, more importantly, why would the
world end if we tweaked that definition for inner classes?
>> For that matter, we could allow outer member access only via
>> 'this', which would prevent anything but one layer of self recursion
>> (i.e. 'this->a.b' where 'this' == '&this->a').
>
> What exactly would that mean? Are you saying that if you're an inner class,
> you can refer to any member of your owning class(es)... except other inner
> class members?
No (if I'm understanding the above). I'm saying that "this" is special:
class Outer
{
<<inner>> class Inner { int x; void foo(); } a;
}
void Outer::Inner::foo()
{
this->x; // okay; our own member
this->a; // okay; access to parent members allowed via 'this'
this->a.a; // error; 'a' is not a member of I1
}
IOW, if a direct member isn't found, the compiler only looks on the
containing classes when the RHS of '->' (or '.') is "this". The effect
is that the "innerness" of a class is only usable within the bodies of
member functions of that class.
A less restrictive form is making lookup on the outer class act as if
the outer class is a protected base. This would prevent using a pointer
to a different class and/or outside a member function to go back out to
the outer class, but will still allow silliness within an inner class
member function w.r.t. members of the same inner class type.
>>>> It occurs to me that this would be more palatable if we had
>>>> something to declare what "looks" like an array of inner
>>>> classes despite being something different under the hood.
>>>
>>> Define "looks".
>>
>> Has an operator[](size_t). In fact, that's pretty much the only
>> requirement I'd place on it.
>
> Then it's not an array. And if you declare one in the exact same way as a
> language array, yet defy the rules of language arrays, then you'll confuse
> people needlessly.
Oh, good grief, no! The *most* the declaration would "look like an
array" would be:
std::inner_class_array<InnerType, 4> props;
More likely it would look like:
InnerType p0, p1, p2, p3;
std::inner_class_array props = { p0, p1, p2, p3 };
I probably should have clarified the original comment: by "something" I
mean a library class template. *NOT* syntax, and *ESPECIALLY NOT* a
perversion of a C-style array type.
The point is (merely) to be able to write, where it makes for a sensible
API:
outer->inner[<<expr>>].<<expr>>;
If one can also write:
o1->inner = o2->inner; // 'inner' is an "array"
....that would be a bonus, and seems like it ought to also be achievable
with the library type.
> You've done more to explain what he meant by "service object" than he did,
> and it's still not clear what that code would look like. That's why actual
> example code is a better example; I'm still not sure what you stream writer
> suggestion would look like.
Lame example:
class Server
{
int total_messages = 0;
<<inner>> class Writer
{
Stream m_s;
public:
Writer(std::istream& s) : m_s(s) {}
write(char* text)
{
m_s << text << std::endl;
++this->total_messages;
}
} m_out = {std::cout}, m_err = {std::cerr};
};
Server::bar()
{
// ...
if (success)
out.write("I feel happy");
else
err.write("Nine pence");
}
Not exactly realistic, but it's a maximally simplified example.
Basically, the idea is you have several instances of
similar-but-not-identical "sub-objects" that need close access to the
outer object. Inner classes allow you to factor out the common pieces
into a class (possible a base class) and have multiple instances as
members of the outer class.
Edward: ping?
--
Matthew
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Thu, 10 Sep 2015 19:04:36 -0700 (PDT)
Raw View
------=_Part_158_493741413.1441937076780
Content-Type: multipart/alternative;
boundary="----=_Part_159_978967772.1441937076780"
------=_Part_159_978967772.1441937076780
Content-Type: text/plain; charset=UTF-8
On Wednesday, September 9, 2015 at 5:58:26 PM UTC-4, Matthew Woehlke wrote:
>
> On 2015-09-09 15:50, Nicol Bolas wrote:
> > On Monday, September 7, 2015 at 4:08:58 PM UTC-4, Matthew Woehlke wrote:
> >> Oh, and this made me think of another interesting question: does a
> >> static member of stateless type have storage? What happens then
> >> when I take the address of such a critter? Is itallowed to be
> >> null?
> >
> > The same thing that happens when you take the address of any stateless
> > object, whether a member or not.
> >
> > You get a pointer of the stateless type, who's memory address may be
> shared
> > with other objects. For a stateless NSDM, it may have the value of its
> > owner's `this`. But it may also point to `this` + 1 byte. Or to
> something
> > else entirely.
>
> Okay, let me rephrase that; is reading data via that pointer UB? If yes,
> then I guess we have no problem here.
>
What do you mean by "reading data"? The object is stateless. Just like a
struct with no members, it has no (non-stateless) NSDMs to "read".
> >> On 2015-09-07 15:04, Nicol Bolas wrote:
> >>> I see no reason to declare this to be undefined behavior. It should
> >>> either be allowed or forbidden.
> >>
> >> I'm not talking about the *copy ctor*; the compiler can easily generate
> >> that to copy only the non-offset bits. I'm talking about *user code*
> >> doing a memcpy/memmove. The suggestion would be that for the *user* to
> >> call memcpy/memmove on an inner class's bytes is UB.
> >
> > There's a term for types where memcpy results in UB: *not trivially
> > copyable*.
>
> Okay... where is that specified?
ISO/IEC 14882:2011, Standard For Programming Language C++, section 3.9, p3:
-> For any trivially copyable type T, if two pointers to T point to
distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a
base-class subobject, if the underlying bytes (1.7) making up obj1 are
copied into obj2, 41 obj2 shall subsequently hold the same value as obj1.
Notice how this paragraph explicitly forbids memcpying base class
subobjects. That's part of how EBO is permitted. Empty objects still
technically have a size and are trivially copyable types. But it isn't
legal to memcpy from or to base class subobjects, so you never have to deal
with the issue of copying from/to something that has no size.
Something similar could be said for stateless object trivial copyability.
That is, memcpying isn't allowed if the source or destination object is a
non-static member subobject. You can memcpy the containing object, but not
the NSDM subobjects, if they are stateless.
Compilers can still optimize space for stateless non-NSDM variables, or at
least concatenate all of them into a single memory location.
And, more importantly, why would the
> world end if we tweaked that definition for inner classes?
>
Because it is not necessary. Limitations should only exist if they're
outside of the design space or if it would be unimplementable without them.
Consider the case I just mentioned, about forbidding memcpy on stateless
NDSM subobjects. That's done *solely* to allow implementations the ability
to give stateless NDSMs zero space. Just like with EBO and memcpying member
subobjects.
If it's possible for compilers to implement inner class NSDMs so that they
can be trivially copyable, then we should make them trivially copyable
(where applicable, of course).
> >>>> It occurs to me that this would be more palatable if we had
> >>>> something to declare what "looks" like an array of inner
> >>>> classes despite being something different under the hood.
> >>>
> >>> Define "looks".
> >>
> >> Has an operator[](size_t). In fact, that's pretty much the only
> >> requirement I'd place on it.
> >
> > Then it's not an array. And if you declare one in the exact same way as
> a
> > language array, yet defy the rules of language arrays, then you'll
> confuse
> > people needlessly.
>
> Oh, good grief, no! The *most* the declaration would "look like an
> array" would be:
>
> std::inner_class_array<InnerType, 4> props;
>
> More likely it would look like:
>
> InnerType p0, p1, p2, p3;
> std::inner_class_array props = { p0, p1, p2, p3 };
>
I'll assume you missed the template argument there, but your general point
is made.
Basically, the idea is you have several instances of
> similar-but-not-identical "sub-objects" that need close access to the
> outer object. Inner classes allow you to factor out the common pieces
> into a class (possible a base class) and have multiple instances as
> members of the outer class.
>
Fair enough.
--
---
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 email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_159_978967772.1441937076780
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Wednesday, September 9, 2015 at 5:58:26 PM UTC-4, Matthew Woehlke wrote:=
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;">On 2015-09-09 15:50, Nicol Bol=
as wrote:
<br>> On Monday, September 7, 2015 at 4:08:58 PM UTC-4, Matthew Woehlke =
wrote:
<br>>> Oh, and this made me think of another interesting question: do=
es a=20
<br>>> static member of stateless type have storage? What happens the=
n=20
<br>>> when I take the address of such a critter? Is itallowed to be=
=20
<br>>> null?
<br>>=20
<br>> The same thing that happens when you take the address of any state=
less=20
<br>> object, whether a member or not.
<br>>=20
<br>> You get a pointer of the stateless type, who's memory address =
may be shared=20
<br>> with other objects. For a stateless NSDM, it may have the value of=
its=20
<br>> owner's `this`. But it may also point to `this` + 1 byte. Or t=
o something=20
<br>> else entirely.
<br>
<br>Okay, let me rephrase that; is reading data via that pointer UB? If yes=
,
<br>then I guess we have no problem here.<br></blockquote><div><br>What do =
you mean by "reading data"? The object is stateless. Just like a =
struct with no members, it has no (non-stateless) NSDMs to "read"=
..<br>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margi=
n-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
>> On 2015-09-07 15:04, Nicol Bolas wrote:=20
<br>>>> I see no reason to declare this to be undefined behavior. =
It should=20
<br>>>> either be allowed or forbidden.=20
<br>>>
<br>>> I'm not talking about the *copy ctor*; the compiler can ea=
sily generate=20
<br>>> that to copy only the non-offset bits. I'm talking about *=
user code*=20
<br>>> doing a memcpy/memmove. The suggestion would be that for the *=
user* to=20
<br>>> call memcpy/memmove on an inner class's bytes is UB.
<br>>=20
<br>> There's a term for types where memcpy results in UB: *not triv=
ially=20
<br>> copyable*.
<br>
<br>Okay... where is that specified?</blockquote><div><br>ISO/IEC 14882:201=
1, Standard For Programming Language C++, section 3.9, p3:<br><br>-> For=
any trivially copyable type T, if two pointers to T point to distinct T ob=
jects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject,=
if the underlying bytes (1.7) making up obj1 are copied into obj2, 41 obj2=
shall subsequently hold the same value as obj1.<br><br>Notice how this par=
agraph explicitly forbids memcpying base class subobjects. That's part =
of how EBO is permitted. Empty objects still technically have a size and ar=
e trivially copyable types. But it isn't legal to memcpy from or to bas=
e class subobjects, so you never have to deal with the issue of copying fro=
m/to something that has no size.<br><br>Something similar could be said for=
stateless object trivial copyability. That is, memcpying isn't allowed=
if the source or destination object is a non-static member subobject. You =
can memcpy the containing object, but not the NSDM subobjects, if they are =
stateless.<br><br>Compilers can still optimize space for stateless non-NSDM=
variables, or at least concatenate all of them into a single memory locati=
on.<br><br></div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margi=
n-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">And, more imp=
ortantly, why would the
<br>world end if we tweaked that definition for inner classes?<br></blockqu=
ote><div><br>Because it is not necessary. Limitations should only exist if =
they're outside of the design space or if it would be unimplementable w=
ithout them.<br><br>Consider the case I just mentioned, about forbidding me=
mcpy on stateless NDSM subobjects. That's done <i>solely</i> to allow i=
mplementations the ability to give stateless NDSMs zero space. Just like wi=
th EBO and memcpying member subobjects.<br><br>If it's possible for com=
pilers to implement inner class NSDMs so that they can be trivially copyabl=
e, then we should make them trivially copyable (where applicable, of course=
).<br>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;marg=
in-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
>>>> It occurs to me that this would be more palatable if we ha=
d
<br>>>>> something to declare what "looks" like an ar=
ray of inner
<br>>>>> classes despite being something different under the ho=
od.
<br>>>>
<br>>>> Define "looks".
<br>>>
<br>>> Has an operator[](size_t). In fact, that's pretty much the=
only=20
<br>>> requirement I'd place on it.
<br>>=20
<br>> Then it's not an array. And if you declare one in the exact sa=
me way as a=20
<br>> language array, yet defy the rules of language arrays, then you=
9;ll confuse=20
<br>> people needlessly.
<br>
<br>Oh, good grief, no! The *most* the declaration would "look like an
<br>array" would be:
<br>
<br>=C2=A0 std::inner_class_array<<wbr>InnerType, 4> props;
<br>
<br>More likely it would look like:
<br>
<br>=C2=A0 InnerType p0, p1, p2, p3;
<br>=C2=A0 std::inner_class_array props =3D { p0, p1, p2, p3 };<br></blockq=
uote><div><br>I'll assume you missed the template argument there, but y=
our general point is made.<br><br></div><blockquote class=3D"gmail_quote" s=
tyle=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-le=
ft: 1ex;">Basically, the idea is you have several instances of
<br>similar-but-not-identical "sub-objects" that need close acces=
s to the
<br>outer object. Inner classes allow you to factor out the common pieces
<br>into a class (possible a base class) and have multiple instances as
<br>members of the outer class.
<br></blockquote><div><br>Fair enough.</div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_159_978967772.1441937076780--
------=_Part_158_493741413.1441937076780--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Fri, 11 Sep 2015 10:56:46 -0400
Raw View
On 2015-09-10 22:04, Nicol Bolas wrote:
> On Wednesday, September 9, 2015 at 5:58:26 PM UTC-4, Matthew Woehlke wrot=
e:
>> Okay, let me rephrase that; is reading data via that pointer UB? If yes,=
=20
>> then I guess we have no problem here.
>=20
> What do you mean by "reading data"?
Dereferencing the pointer in a way that causes a read access to memory.
(Or write, for that matter.)
> The object is stateless. Just like a=20
> struct with no members, it has no (non-stateless) NSDMs to "read".
Stateless* src =3D ...;
memcpy(somewhere, src, sizeof(Stateless);
Is that not UB? (Not talking about the actual data that ends up in
'somewhere', which presumably is unspecified, but i.e. that it won't
SEGV, launch missles, etc.) If it isn't UB, I would think that any
stateless object would need to have "real storage".
If it's UB, then 'src' could be anything, including null.
>> On 2015-09-09 15:50, Nicol Bolas wrote:=20
>>> There's a term for types where memcpy results in UB: *not trivially=20
>>> copyable*.=20
>>
>> Okay... where is that specified?
>=20
> ISO/IEC 14882:2011, Standard For Programming Language C++, section 3.9, p=
3:
>=20
> -> For any trivially copyable type T, if two pointers to T point to=20
> distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a=20
> base-class subobject, if the underlying bytes (1.7) making up obj1 are=20
> copied into obj2, 41 obj2 shall subsequently hold the same value as obj1.
>=20
> Notice how this paragraph explicitly forbids memcpying base class=20
> subobjects. That's part of how EBO is permitted. Empty objects still=20
> technically have a size and are trivially copyable types. But it isn't=20
> legal to memcpy from or to base class subobjects, so you never have to de=
al=20
> with the issue of copying from/to something that has no size.
>=20
> Something similar could be said for stateless object trivial copyability.=
=20
> That is, memcpying isn't allowed if the source or destination object is a=
=20
> non-static member subobject. You can memcpy the containing object, but no=
t=20
> the NSDM subobjects, if they are stateless.
I think I'd extend that to *any* stateless objects. There's no reason to
copy them in the first place. (And I think it addresses the above.)
(Don't forget to make the same change to 3.9=C2=B62 also :-).)
>> And, more importantly, why would the world end if we tweaked that
>> definition for inner classes?
>=20
> Because it is not necessary. Limitations should only exist if they're=20
> outside of the design space or if it would be unimplementable without the=
m.
Define "necessary". We have two possible implementations: offset part of
inner class type, and offset adjacent to inner class type. One precludes
trivially copyability, the other arrays, both of which seem desirable
features. That seems to me like a case of "unimplementable without [new
limitations]" / "necessary".
An exception/clarification to the memcpy rule could permit both.
OTOH, maybe doing that is less desirable than supporting true arrays of
inner classes.
I'm still not convinced it's *impossible*, but maybe I am convinced it's
*impractical* :-).
>>>>>> It occurs to me that this would be more palatable if we had=20
>>>>>> something to declare what "looks" like an array of inner=20
>>>>>> classes despite being something different under the hood.=20
>>>>>
>>>>> Define "looks".=20
>>>>
>>>> Has an operator[](size_t). In fact, that's pretty much the only=20
>>>> requirement I'd place on it.=20
>>>
>>> Then it's not an array. And if you declare one in the exact same
>>> way as a language array, yet defy the rules of language arrays,
>>> then you'll confuse people needlessly.
>>
>> Oh, good grief, no! The *most* the declaration would "look like an=20
>> array" would be:=20
>>
>> std::inner_class_array<InnerType, 4> props;=20
>>
>> More likely it would look like:=20
>>
>> InnerType p0, p1, p2, p3;=20
>> std::inner_class_array props =3D { p0, p1, p2, p3 };
>=20
> I'll assume you missed the template argument there, but your general poin=
t=20
> is made.
....IIRC, I was being optimistic about template argument deduction ;-).
(Perhaps unreasonably so...)
Anyway, I'm fine if we don't have this. It's more something to keep in
reserve that could mitigate the prohibition on arrays. (If we aren't
able to lift it anyway via some resolution of the above TC discussion.)
--=20
Matthew
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Fri, 11 Sep 2015 10:12:35 -0700 (PDT)
Raw View
------=_Part_766_2001983876.1441991555334
Content-Type: multipart/alternative;
boundary="----=_Part_767_199349892.1441991555334"
------=_Part_767_199349892.1441991555334
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Friday, September 11, 2015 at 10:57:05 AM UTC-4, Matthew Woehlke wrote:
>
> On 2015-09-10 22:04, Nicol Bolas wrote:=20
> > On Wednesday, September 9, 2015 at 5:58:26 PM UTC-4, Matthew Woehlke=20
> wrote:=20
> >> Okay, let me rephrase that; is reading data via that pointer UB? If=20
> yes,=20
> >> then I guess we have no problem here.=20
> >=20
> > What do you mean by "reading data"?=20
>
> Dereferencing the pointer in a way that causes a read access to memory.=
=20
> (Or write, for that matter.)=20
>
> > The object is stateless. Just like a=20
> > struct with no members, it has no (non-stateless) NSDMs to "read".=20
>
> Stateless* src =3D ...;=20
> memcpy(somewhere, src, sizeof(Stateless);=20
>
> Is that not UB?
Using the rules I outlined, it depends. If `src` is a member subobject,=20
then it's UB. If `somewhere` is a member subobject, then it's UB. If=20
neither are member subobjects, then it's fine.
Remember: empty base optimization is not *required*, except for standard=20
layout rules. Similarly, stateless optimization is not required, except for=
=20
member subobjects. The compiler could give all static-duration stateless=20
objects the same memory location. The compiler could give=20
automatic-duration stateless objects no location, unless the user gives=20
them one. Or it could give them the global, static-duration location. Or=20
one location on the stack just like regular empty objects.
=20
> >> On 2015-09-09 15:50, Nicol Bolas wrote:=20
> >>> There's a term for types where memcpy results in UB: *not trivially=
=20
> >>> copyable*.=20
> >>=20
> >> Okay... where is that specified?=20
> >=20
> > ISO/IEC 14882:2011, Standard For Programming Language C++, section 3.9,=
=20
> p3:=20
> >=20
> > -> For any trivially copyable type T, if two pointers to T point to=20
> > distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a=20
> > base-class subobject, if the underlying bytes (1.7) making up obj1 are=
=20
> > copied into obj2, 41 obj2 shall subsequently hold the same value as=20
> obj1.=20
> >=20
> > Notice how this paragraph explicitly forbids memcpying base class=20
> > subobjects. That's part of how EBO is permitted. Empty objects still=20
> > technically have a size and are trivially copyable types. But it isn't=
=20
> > legal to memcpy from or to base class subobjects, so you never have to=
=20
> deal=20
> > with the issue of copying from/to something that has no size.=20
> >=20
> > Something similar could be said for stateless object trivial=20
> copyability.=20
> > That is, memcpying isn't allowed if the source or destination object is=
=20
> a=20
> > non-static member subobject. You can memcpy the containing object, but=
=20
> not=20
> > the NSDM subobjects, if they are stateless.=20
>
> I think I'd extend that to *any* stateless objects. There's no reason to=
=20
> copy them in the first place. (And I think it addresses the above.)=20
>
> (Don't forget to make the same change to 3.9=C2=B62 also :-).)=20
>
> >> And, more importantly, why would the world end if we tweaked that=20
> >> definition for inner classes?=20
> >=20
> > Because it is not necessary. Limitations should only exist if they're=
=20
> > outside of the design space or if it would be unimplementable without=
=20
> them.=20
>
> Define "necessary". We have two possible implementations: offset part of=
=20
> inner class type, and offset adjacent to inner class type. One precludes=
=20
> trivially copyability, the other arrays, both of which seem desirable=20
> features. That seems to me like a case of "unimplementable without [new=
=20
> limitations]" / "necessary".
>
An exception/clarification to the memcpy rule could permit both.=20
>
And you lose the *whole point* of trivial copyability: the ability to do a=
=20
memcpy on it.
This isn't like the exception made for base subobjects with respect to=20
memcpy. In that case, it's not the type itself that's the problem; it's a=
=20
specific use-case: being a base subobject. Here, *every instance* of an=20
inner class must be a member subobject. Therefore, while you can "say" that=
=20
it's trivially copyable, you can never actually trivially copy it.
So what's the point of saying it's trivially copyable... if you can't=20
actually copy it trivially?
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
------=_Part_767_199349892.1441991555334
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Friday, September 11, 2015 at 10:57:05 AM UTC-4, Matthe=
w Woehlke wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin=
-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">On 2015-09-10 =
22:04, Nicol Bolas wrote:
<br>> On Wednesday, September 9, 2015 at 5:58:26 PM UTC-4, Matthew Woehl=
ke wrote:
<br>>> Okay, let me rephrase that; is reading data via that pointer U=
B? If yes,=20
<br>>> then I guess we have no problem here.
<br>>=20
<br>> What do you mean by "reading data"?
<br>
<br>Dereferencing the pointer in a way that causes a read access to memory.
<br>(Or write, for that matter.)
<br>
<br>> The object is stateless. Just like a=20
<br>> struct with no members, it has no (non-stateless) NSDMs to "r=
ead".
<br>
<br>=C2=A0 Stateless* src =3D ...;
<br>=C2=A0 memcpy(somewhere, src, sizeof(Stateless);
<br>
<br>Is that not UB?</blockquote><div><br>Using the rules I outlined, it dep=
ends. If `src` is a member subobject, then it's UB. If `somewhere` is a=
member subobject, then it's UB. If neither are member subobjects, then=
it's fine.<br><br>Remember: empty base optimization is not <i>required=
</i>, except for standard layout rules. Similarly, stateless optimization i=
s not required, except for member subobjects. The compiler could give all s=
tatic-duration stateless objects the same memory location. The compiler cou=
ld give automatic-duration stateless objects no location, unless the user g=
ives them one. Or it could give them the global, static-duration location. =
Or one location on the stack just like regular empty objects.<br>=C2=A0</di=
v><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;b=
order-left: 1px #ccc solid;padding-left: 1ex;"> >> On 2015-09-09 15:5=
0, Nicol Bolas wrote:=20
<br>>>> There's a term for types where memcpy results in UB: *=
not trivially=20
<br>>>> copyable*.=20
<br>>>
<br>>> Okay... where is that specified?
<br>>=20
<br>> ISO/IEC 14882:2011, Standard For Programming Language C++, section=
3.9, p3:
<br>>=20
<br>> -> For any trivially copyable type T, if two pointers to T poin=
t to=20
<br>> distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a=
=20
<br>> base-class subobject, if the underlying bytes (1.7) making up obj1=
are=20
<br>> copied into obj2, 41 obj2 shall subsequently hold the same value a=
s obj1.
<br>>=20
<br>> Notice how this paragraph explicitly forbids memcpying base class=
=20
<br>> subobjects. That's part of how EBO is permitted. Empty objects=
still=20
<br>> technically have a size and are trivially copyable types. But it i=
sn't=20
<br>> legal to memcpy from or to base class subobjects, so you never hav=
e to deal=20
<br>> with the issue of copying from/to something that has no size.
<br>>=20
<br>> Something similar could be said for stateless object trivial copya=
bility.=20
<br>> That is, memcpying isn't allowed if the source or destination =
object is a=20
<br>> non-static member subobject. You can memcpy the containing object,=
but not=20
<br>> the NSDM subobjects, if they are stateless.
<br>
<br>I think I'd extend that to *any* stateless objects. There's no =
reason to
<br>copy them in the first place. (And I think it addresses the above.)
<br>
<br>(Don't forget to make the same change to 3.9=C2=B62 also :-).)
<br>
<br>>> And, more importantly, why would the world end if we tweaked t=
hat
<br>>> definition for inner classes?
<br>>=20
<br>> Because it is not necessary. Limitations should only exist if they=
're=20
<br>> outside of the design space or if it would be unimplementable with=
out them.
<br>
<br>Define "necessary". We have two possible implementations: off=
set part of
<br>inner class type, and offset adjacent to inner class type. One preclude=
s
<br>trivially copyability, the other arrays, both of which seem desirable
<br>features. That seems to me like a case of "unimplementable without=
[new
<br>limitations]" / "necessary".<br></blockquote><div></div>=
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;">
An exception/clarification to the memcpy rule could permit both.
<br></blockquote><div><br>And you lose the <i>whole point</i> of trivial co=
pyability: the ability to do a memcpy on it.<br><br>This isn't like the=
exception made for base subobjects with respect to memcpy. In that case, i=
t's not the type itself that's the problem; it's a specific use=
-case: being a base subobject. Here, <i>every instance</i> of an inner clas=
s must be a member subobject. Therefore, while you can "say" that=
it's trivially copyable, you can never actually trivially copy it.<br>=
<br>So what's the point of saying it's trivially copyable... if you=
can't actually copy it trivially?<br></div></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_767_199349892.1441991555334--
------=_Part_766_2001983876.1441991555334--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Fri, 11 Sep 2015 13:54:09 -0400
Raw View
On 2015-09-11 13:12, Nicol Bolas wrote:
> On Friday, September 11, 2015 at 10:57:05 AM UTC-4, Matthew Woehlke wrote=
:
>> Stateless* src =3D ...;=20
>> memcpy(somewhere, src, sizeof(Stateless);=20
>>
>> Is that not UB?
>=20
> Using the rules I outlined, it depends. If `src` is a member subobject,=
=20
> then it's UB. If `somewhere` is a member subobject, then it's UB. If=20
> neither are member subobjects, then it's fine.
So...
void foo()
{
static Stateless s;
char buf[sizeof(Stateless)];
memcpy(buf, &s, sizeof(Stateless));
}
....is the above well-formed and not UB? Then, going back to my original
question:
>> Oh, and this made me think of another interesting question: does a
>> static member of stateless type have storage? I want to say "yes" (or
>> possibly "implementation defined"), so that you can do things like take
>> its address.
....the answer would I think have to be "yes"? (Note: I'm not sure what I
was thinking when I specified "member" there; I meant to include
non-members e.g. globals and local statics also. I guess non-static
locals - or even all locals - could depend on if their address is ever
taken.)
To be clear, this is by no means an objection, just a clarification. I'm
okay with either behavior, just so long as it's clear what is allowed.
>> We have two possible implementations: offset part of inner class
>> type, and offset adjacent to inner class type. One precludes=20
>> trivially copyability, the other arrays, both of which seem
>> desirable features. That seems to me like a case of
>> "unimplementable without [new limitations]" / "necessary".
>>
>> An exception/clarification to the memcpy rule could permit both.=20
>=20
> And you lose the *whole point* of trivial copyability: the ability to do =
a=20
> memcpy on it.
I'm not sure I agree that *the user* being able to do a memcpy is "the
whole point of trivial copyability". I would say rather that *the
compiler* being able to do so is of main importance.
An exception doesn't break the critical use cases:
- The outer class, including inner class members, may still be
trivially copied, including via na=C3=AFve memcpy.
- The inner class copy/move/assign may still have a default generated
implementation that is a simple memcpy.
....and the user can still copy *one* inner class type (but not an array
thereof) via memcpy, albeit the arguments must be non-standard.
Hmm... I guess the problem you are concerned about is:
template <typename T>
legacy_code(T& out, T const& in)
{
// ...
if (/* T is trivially copyable */)
{
memcpy(&out, &in, sizeof(T));
}
// ...
}
....?
(And why are you even writing such code? The only instances I can think
of where an explicit memcpy is useful are in container types, where T
will never be an inner class type.)
--=20
Matthew
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Sat, 12 Sep 2015 08:36:51 -0700 (PDT)
Raw View
------=_Part_35_140267755.1442072211911
Content-Type: multipart/alternative;
boundary="----=_Part_36_2031310653.1442072212643"
------=_Part_36_2031310653.1442072212643
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Friday, September 11, 2015 at 1:54:35 PM UTC-4, Matthew Woehlke wrote:
>
> On 2015-09-11 13:12, Nicol Bolas wrote:=20
> > On Friday, September 11, 2015 at 10:57:05 AM UTC-4, Matthew Woehlke=20
> wrote:=20
> >> Stateless* src =3D ...;=20
> >> memcpy(somewhere, src, sizeof(Stateless);=20
> >>=20
> >> Is that not UB?=20
> >=20
> > Using the rules I outlined, it depends. If `src` is a member subobject,=
=20
> > then it's UB. If `somewhere` is a member subobject, then it's UB. If=20
> > neither are member subobjects, then it's fine.=20
>
> So...=20
>
> void foo()=20
> {=20
> static Stateless s;=20
> char buf[sizeof(Stateless)];=20
> memcpy(buf, &s, sizeof(Stateless));=20
> }=20
>
> ...is the above well-formed and not UB?
Yes.
=20
> Then, going back to my original=20
> question:=20
>
> >> Oh, and this made me think of another interesting question: does a=20
> >> static member of stateless type have storage? I want to say "yes" (or=
=20
> >> possibly "implementation defined"), so that you can do things like tak=
e=20
> >> its address.=20
>
> ...the answer would I think have to be "yes"?
*Every object* has storage, so that question was answered by the standard.=
=20
Variables of stateless types are objects, no matter where they are=20
declared. Therefore, they have storage.
The question is whether they have *unique* storage.
Compilers are allowed to give objects storage or not, within the limits of=
=20
observable behavior. A compiler may optimize the placement of an `int` into=
=20
a register, so that it never has actual "storage" in memory. This would=20
only be possible if you never take the address of that integer.
The same would be true of stateless types. If you never take the address of=
=20
a non-NSDM stateless object, the compiler doesn't have to give it storage.=
=20
If you do take the address of such an object, the compiler will have to=20
ensure that it has some address.
But stateless objects are not required to have unique addresses. So in the=
=20
above case, if the compiler needs to give storage to a non-NSDM stateless=
=20
object, the compiler could give it the exact same storage that it gives=20
*all* such objects.
>> We have two possible implementations: offset part of inner class=20
> >> type, and offset adjacent to inner class type. One precludes=20
> >> trivially copyability, the other arrays, both of which seem=20
> >> desirable features. That seems to me like a case of=20
> >> "unimplementable without [new limitations]" / "necessary".=20
> >>=20
> >> An exception/clarification to the memcpy rule could permit both.=20
> >=20
> > And you lose the *whole point* of trivial copyability: the ability to d=
o=20
> a=20
> > memcpy on it.=20
>
> I'm not sure I agree that *the user* being able to do a memcpy is "the=20
> whole point of trivial copyability". I would say rather that *the=20
> compiler* being able to do so is of main importance.
>
You can believe whatever you like, but the actual reasoning for the change=
=20
is quite clear. It's there so that users can do `memcpy` on objects rather=
=20
than calling contructors. `std::vector` implements this as an optimization=
=20
when copying them or reallocating storage.
Outside of defining what types are trivially copyable, that trivially=20
copyable objects can be copied via `memcpy` is the *only thing* the=20
standard says about them. So it's kinda hard to see how the main point of=
=20
something isn't the only thing that is said about them.
The old C++98/03 rules allowed such copying only for POD types. The trivial=
=20
copyable distinction was introduced because POD was too big of a=20
restriction, that compilers could allow more complex objects to be copied=
=20
bytewise.
How the compiler implements the default copy constructor was not a factor=
=20
in the creation of this category. After all, the user can't tell the=20
difference between a copy-constructor-via-memcpy and a=20
copy-constructor-via-memberwise-copy. That's optimization, not apparent=20
behavior.
And even then, I'd bet that compilers will decide on memberwise-copies when=
=20
a bytewise-loop would be too expensive. That's all internal compiler=20
optimizations; they have nothing to do with the trivial copyability=20
category.
Or to put it another way, people aren't using metaprogramming to check to=
=20
see if something is trivially copyable so that they can tell when a copy=20
constructor will be "fast" or "slow". It's to tell if they need to use the=
=20
copy constructor *at all*.
An exception doesn't break the critical use cases:=20
>
> - The outer class, including inner class members, may still be=20
> trivially copied, including via na=C3=AFve memcpy.
>
OK, the trivial copyability rules are pretty simple as they stand now. In=
=20
particular, the rule for NSDMs and base classes is, well, trivial: they=20
must themselves be trivially copyable.
What you're proposing is to take this very simple rule and highly=20
complicate it:
-> All NSDMs must be trivially copyable, except for NSDMs of inner class=20
types. A class may be trivially copyable with inner class NSDMs if those=20
types would satisfy all of the requirements of trivial copyability, save=20
the one forbidding inner classes from being trivially copyable.
So a class is trivially copyable even though it has members that aren't=20
themselves trivially copyable. Except that those non-trivially copyable=20
member *can* still affect trivial copyability of the other class, if the=20
reason why they're not trivially copyable is one of a subset of the=20
prohibitions of trivial copyability.
Go ahead and explain *that* to somebody...
- The inner class copy/move/assign may still have a default generated=20
> implementation that is a simple memcpy.
>
I think you're misunderstanding something here.
It doesn't matter whether the default copy constructor can be implemented=
=20
as a memcpy. That's not what trivial copyability is about. It's about=20
whether calling the copy constructor is completely equivalent to=20
`memcpy(..., sizeof(T))`.
=20
> ...and the user can still copy *one* inner class type (but not an array=
=20
> thereof) via memcpy, albeit the arguments must be non-standard.=20
>
The same misunderstanding is at play here. If the copy size is not=20
`sizeof(T)`, then it's not a trivial copy.
Hmm... I guess the problem you are concerned about is:=20
>
> template <typename T>=20
> legacy_code(T& out, T const& in)=20
> {=20
> // ...=20
> if (/* T is trivially copyable */)=20
> {=20
> memcpy(&out, &in, sizeof(T));=20
> }=20
> // ...=20
> }=20
>
> ...?=20
>
> (And why are you even writing such code?
Just off the top of my head, binary serialization.
--=20
---=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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
------=_Part_36_2031310653.1442072212643
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Friday, September 11, 2015 at 1:54:35 PM UTC-4, Matthew Woehlke wrote:<b=
lockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;borde=
r-left: 1px #ccc solid;padding-left: 1ex;">On 2015-09-11 13:12, Nicol Bolas=
wrote:
<br>> On Friday, September 11, 2015 at 10:57:05 AM UTC-4, Matthew Woehlk=
e wrote:
<br>>> =C2=A0 Stateless* src =3D ...;=20
<br>>> =C2=A0 memcpy(somewhere, src, sizeof(Stateless);=20
<br>>>
<br>>> Is that not UB?
<br>>=20
<br>> Using the rules I outlined, it depends. If `src` is a member subob=
ject,=20
<br>> then it's UB. If `somewhere` is a member subobject, then it=
9;s UB. If=20
<br>> neither are member subobjects, then it's fine.
<br>
<br>So...
<br>
<br>=C2=A0 void foo()
<br>=C2=A0 {
<br>=C2=A0 =C2=A0 static Stateless s;
<br>=C2=A0 =C2=A0 char buf[sizeof(Stateless)];
<br>=C2=A0 =C2=A0 memcpy(buf, &s, sizeof(Stateless));
<br>=C2=A0 }
<br>
<br>...is the above well-formed and not UB?</blockquote><div><br>Yes.<br><b=
r>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-l=
eft: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"> Then, going bac=
k to my original
<br>question:
<br>
<br>>> Oh, and this made me think of another interesting question: do=
es a
<br>>> static member of stateless type have storage? I want to say &q=
uot;yes" (or
<br>>> possibly "implementation defined"), so that you can =
do things like take
<br>>> its address.
<br>
<br>...the answer would I think have to be "yes"?</blockquote><di=
v><br><i>Every object</i> has storage, so that question was answered by the=
standard. Variables of stateless types are objects, no matter where they a=
re declared. Therefore, they have storage.<br><br>The question is whether t=
hey have <i>unique</i> storage.<br><br>Compilers are allowed to give object=
s storage or not, within the limits of observable behavior. A compiler may =
optimize the placement of an `int` into a register, so that it never has ac=
tual "storage" in memory. This would only be possible if you neve=
r take the address of that integer.<br><br>The same would be true of statel=
ess types. If you never take the address of a non-NSDM stateless object, th=
e compiler doesn't have to give it storage. If you do take the address =
of such an object, the compiler will have to ensure that it has some addres=
s.<br><br>But stateless objects are not required to have unique addresses. =
So in the above case, if the compiler needs to give storage to a non-NSDM s=
tateless object, the compiler could give it the exact same storage that it =
gives <i>all</i> such objects.<br><br></div><blockquote class=3D"gmail_quot=
e" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;paddin=
g-left: 1ex;">>> We have two possible implementations: offset part of=
inner class
<br>>> type, and offset adjacent to inner class type. One precludes=
=20
<br>>> trivially copyability, the other arrays, both of which seem
<br>>> desirable features. That seems to me like a case of
<br>>> "unimplementable without [new limitations]" / "=
necessary".
<br>>>
<br>>> An exception/clarification to the memcpy rule could permit bot=
h.=20
<br>>=20
<br>> And you lose the *whole point* of trivial copyability: the ability=
to do a=20
<br>> memcpy on it.
<br>
<br>I'm not sure I agree that *the user* being able to do a memcpy is &=
quot;the
<br>whole point of trivial copyability". I would say rather that *the
<br>compiler* being able to do so is of main importance.<br></blockquote><d=
iv><br>You can believe whatever you like, but the actual reasoning for the =
change is quite clear. It's there so that users can do `memcpy` on obje=
cts rather than calling contructors. `std::vector` implements this as an op=
timization when copying them or reallocating storage.<br><br>Outside of def=
ining what types are trivially copyable, that trivially copyable objects ca=
n be copied via `memcpy` is the <i>only thing</i> the standard says about t=
hem. So it's kinda hard to see how the main point of something isn'=
t the only thing that is said about them.<br><br>The old C++98/03 rules all=
owed such copying only for POD types. The trivial copyable distinction was =
introduced because POD was too big of a restriction, that compilers could a=
llow more complex objects to be copied bytewise.<br><br>How the compiler im=
plements the default copy constructor was not a factor in the creation of t=
his category. After all, the user can't tell the difference between a c=
opy-constructor-via-memcpy and a copy-constructor-via-memberwise-copy. That=
's optimization, not apparent behavior.<br><br>And even then, I'd b=
et that compilers will decide on memberwise-copies when a bytewise-loop wou=
ld be too expensive. That's all internal compiler optimizations; they h=
ave nothing to do with the trivial copyability category.<br><br>Or to put i=
t another way, people aren't using metaprogramming to check to see if s=
omething is trivially copyable so that they can tell when a copy constructo=
r will be "fast" or "slow". It's to tell if they ne=
ed to use the copy constructor <i>at all</i>.<br><br></div><blockquote clas=
s=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #c=
cc solid;padding-left: 1ex;">
An exception doesn't break the critical use cases:
<br>
<br>- The outer class, including inner class members, may still be
<br>=C2=A0 trivially copied, including via na=C3=AFve memcpy.<br></blockquo=
te><div><br>OK, the trivial copyability rules are pretty simple as they sta=
nd now. In particular, the rule for NSDMs and base classes is, well, trivia=
l: they must themselves be trivially copyable.<br><br>What you're propo=
sing is to take this very simple rule and highly complicate it:<br><br>->=
; All NSDMs must be trivially copyable, except for NSDMs of inner class typ=
es. A class may be trivially copyable with inner class NSDMs if those types=
would satisfy all of the requirements of trivial copyability, save the one=
forbidding inner classes from being trivially copyable.<br><br>So a class =
is trivially copyable even though it has members that aren't themselves=
trivially copyable. Except that those non-trivially copyable member <i>can=
</i> still affect trivial copyability of the other class, if the reason why=
they're not trivially copyable is one of a subset of the prohibitions =
of trivial copyability.<br><br>Go ahead and explain <i>that</i> to somebody=
....<br><br></div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margi=
n-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
- The inner class copy/move/assign may still have a default generated
<br>=C2=A0 implementation that is a simple memcpy.<br></blockquote><div><br=
>I think you're misunderstanding something here.<br><br>It doesn't =
matter whether the default copy constructor can be implemented as a memcpy.=
That's not what trivial copyability is about. It's about whether c=
alling the copy constructor is completely equivalent to `memcpy(..., sizeof=
(T))`.<br>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;=
margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">
....and the user can still copy *one* inner class type (but not an array
<br>thereof) via memcpy, albeit the arguments must be non-standard.
<br></blockquote><div><br>The same misunderstanding is at play here. If the=
copy size is not `sizeof(T)`, then it's not a trivial copy.<br><br></d=
iv><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;=
border-left: 1px #ccc solid;padding-left: 1ex;">
Hmm... I guess the problem you are concerned about is:
<br>
<br>=C2=A0 template <typename T>
<br>=C2=A0 legacy_code(T& out, T const& in)
<br>=C2=A0 {
<br>=C2=A0 =C2=A0 // ...
<br>=C2=A0 =C2=A0 if (/* T is trivially copyable */)
<br>=C2=A0 =C2=A0 {
<br>=C2=A0 =C2=A0 =C2=A0 memcpy(&out, &in, sizeof(T));
<br>=C2=A0 =C2=A0 }
<br>=C2=A0 =C2=A0 // ...
<br>=C2=A0 }
<br>
<br>...?
<br>
<br>(And why are you even writing such code?</blockquote><div><br>Just off =
the top of my head, binary serialization.</div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" 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 />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_36_2031310653.1442072212643--
------=_Part_35_140267755.1442072211911--
.