220 4759 <c28d0be7-5dea-462a-915c-3da36519cafa@isocpp.org> article
Path: news.gmane.org!not-for-mail
From: Nikolay Ivchenkov <mk.ivchenkov@gmail.com>
Newsgroups: gmane.comp.lang.c++.isocpp.proposals
Subject: Implementability of std::optional
Date: Fri, 31 May 2013 05:48:11 -0700 (PDT)
Lines: 364
Approved: news@gmane.org
Message-ID: <c28d0be7-5dea-462a-915c-3da36519cafa@isocpp.org>
Reply-To: std-proposals@isocpp.org
NNTP-Posting-Host: plane.gmane.org
Mime-Version: 1.0
Content-Type: multipart/alternative; 
	boundary="----=_Part_41_24142763.1370004491584"
X-Trace: ger.gmane.org 1370004494 2756 80.91.229.3 (31 May 2013 12:48:14 GMT)
X-Complaints-To: usenet@ger.gmane.org
NNTP-Posting-Date: Fri, 31 May 2013 12:48:14 +0000 (UTC)
To: std-proposals@isocpp.org
Original-X-From: std-proposals+bncBDJK3XNWR4LRBDNYUKGQKGQEMWWNMWQ@isocpp.org Fri May 31 14:48:16 2013
Return-path: <std-proposals+bncBDJK3XNWR4LRBDNYUKGQKGQEMWWNMWQ@isocpp.org>
Envelope-to: gclcip-std-proposals@m.gmane.org
Original-Received: from mail-vb0-f70.google.com ([209.85.212.70])
	by plane.gmane.org with esmtp (Exim 4.69)
	(envelope-from <std-proposals+bncBDJK3XNWR4LRBDNYUKGQKGQEMWWNMWQ@isocpp.org>)
	id 1UiOkg-0006NZ-QO
	for gclcip-std-proposals@m.gmane.org; Fri, 31 May 2013 14:48:15 +0200
Original-Received: by mail-vb0-f70.google.com with SMTP id f13sf1747062vbg.5
        for <gclcip-std-proposals@m.gmane.org>; Fri, 31 May 2013 05:48:14 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=gmail.com; s=20120113;
        h=x-beenthere:date:from:to:message-id:subject:mime-version
         :x-original-sender:reply-to:precedence:mailing-list:list-id
         :x-google-group-id:list-post:list-help:list-archive:list-subscribe
         :list-unsubscribe:content-type;
        bh=c6ILUt2VN3XFtKPEdgbemxOwwinRAzdgNCsvh2xRge0=;
        b=Qvj5r7Z0QVjrCiMk5ZDIruPt9lcaf4KKz739JAF1dVRsx0bJCQRtq+gK9xoBvcDwYf
         Qqxfb9lHJTeCtMB5YiC9eoNltH8W3Hl3pPkpPWCGFsQWHKqfKkCit+i8S0ybasMKHGIr
         /WYkjSgpQUGFbmNo5uCfqZO/f6vuucb7C8EuIyev95B9DB4CvKJGFBW346RgQiE3+jDR
         HjcIajGrD1qryyb0Bage+Ie8OM7bn1VnkbMFdY7FAPpdfANQDEF/x2aayVTLuUklS3qz
         qBO5YKh0iQjc8xU4Qycoeg1dGaAGkGBdUdPxSdYbfMYiA0tRzuwKC5XGfN7ucu23UyDQ
         KHiw==
X-Received: by 10.236.15.34 with SMTP id e22mr6101980yhe.16.1370004493917;
        Fri, 31 May 2013 05:48:13 -0700 (PDT)
X-BeenThere: std-proposals@isocpp.org
Original-Received: by 10.49.121.5 with SMTP id lg5ls888536qeb.21.gmail; Fri, 31 May
 2013 05:48:12 -0700 (PDT)
X-Received: by 10.49.71.135 with SMTP id v7mr934898qeu.22.1370004492656;
        Fri, 31 May 2013 05:48:12 -0700 (PDT)
X-Original-Sender: mk.ivchenkov@gmail.com
Precedence: list
Mailing-list: list std-proposals@isocpp.org; contact std-proposals+owners@isocpp.org
List-ID: <std-proposals.isocpp.org>
X-Google-Group-Id: 399137483710
List-Post: <http://groups.google.com/a/isocpp.org/group/std-proposals/post?hl=en>,
 <mailto:std-proposals@isocpp.org>
List-Help: <http://support.google.com/a/isocpp.org/bin/topic.py?hl=en&topic=25838>,
 <mailto:std-proposals+help@isocpp.org>
List-Archive: <http://groups.google.com/a/isocpp.org/group/std-proposals/?hl=en>
List-Subscribe: <http://groups.google.com/a/isocpp.org/group/std-proposals/subscribe?hl=en>,
 <mailto:std-proposals+subscribe@isocpp.org>
List-Unsubscribe: <http://groups.google.com/a/isocpp.org/group/std-proposals/subscribe?hl=en>,
 <mailto:googlegroups-manage+399137483710+unsubscribe@googlegroups.com>
Xref: news.gmane.org gmane.comp.lang.c++.isocpp.proposals:4759
Archived-At: <http://permalink.gmane.org/gmane.comp.lang.c++.isocpp.proposals/4759>

------=_Part_41_24142763.1370004491584
Content-Type: text/plain; charset=windows-1252
Content-Transfer-Encoding: quoted-printable

[This thread is based on the recent discussion "Implementation of=20
assignment in std::optional and Core issue 1404: Object reallocation in=20
unions" on SG12 reflector (about undefined/unspecified behavior)].

----------------------------------------------------------------------

The following concerns are related to the suggested implementation of=20
optional - see
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3672.html
https://github.com/akrzemi1/Optional/blob/master/optional.hpp

Consider the following example:

    #include "optional.hpp"
    #include <iostream>

    struct A
    {
        constexpr A(int &x) : ref(x) {}
        int &ref;
    };

    int main()
    {
        int n1 =3D 0, n2 =3D 0;
        std::experimental::optional<A> opt =3D A(n1);
        opt.emplace(n2);
        opt->ref =3D 1;
        std::cout << n1 << " " << n2 << std::endl;
    }

Here initialization of variable opt implies initialization of union member=
=20
storage_.value_ (which has type A). Then expression opt.emplace(n2)destroys=
 object=20
storage_.value_ via explicit destructor call and creates new object by=20
placement form of new-expression (using forwarded n2 in the=20
new-initializer). All public functions that provide access to the stored=20
value (operator->(), operator *(), value(), etc.), obtain pointer/reference=
=20
through object expression storage_.value_.

This is a simplified version of the above code:

    #include <iostream>

    #define FORWARD(x) static_cast<decltype(x) &&>(x)

    template <class T>
        union U
    {
        constexpr U(T &&x) : value_(FORWARD(x)) {}
        unsigned char dummy_;
        T value_;
    };

    template <class T>
        struct optional
    {
        constexpr optional(T &&x) : storage_(FORWARD(x)) {}
        template <class... Params>
            void emplace(Params &&... params)
        {
            storage_.value_.~T();
            new (&storage_.value_) T(FORWARD(params)...);
        }

        U<T> storage_;
    };

    struct A
    {
        constexpr A(int &x) : ref(x) {}
        int &ref;
    };

    int main()
    {
        int n1 =3D 0, n2 =3D 0;
        optional<A> opt2 =3D A(n1);
        opt2.emplace(n2);
        opt2.storage_.value_.ref =3D 1;
        std::cout << n1 << " " << n2 << std::endl;
    }

The question is: What may happen at line

    opt->ref =3D 1;

in the former code or

    opt2.storage_.value_.ref =3D 1;

in the latter (simplified) code?

According to N3485 - 3.8/7,

    If, after the lifetime of an object has ended and before the
    storage which the object occupied is reused or released, a new
    object is created at the storage location which the original
    object occupied, a pointer that pointed to the original object, a
    reference that referred to the original object, or the name of the
    original object will automatically refer to the new object and,
    once the lifetime of the new object has started, can be used to
    manipulate the new object, if:

    [...]
    =97 the type of the original object is not const-qualified, and, if
      a class type, does not contain any non-static data member whose
      type is const-qualified or a reference type, and
    [...]

In our case the cited condition is not satisfied, because A has a=20
non-static data member of a reference type =97 ref.

I presume that the intention behind 3.8/7 was to allow optimizations=20
described below:

    #include <iostream>

    struct X
    {
        int &ref;
    };

    void f(X &);

    int main()
    {
        int n =3D 0;
        X x{n};
        f(x);
        x.ref =3D 5;
        std::cout << n << std::endl;
    }

Here a compiler is allowed to assume that

    x.ref =3D 5;

is equivalent to

    n =3D 5;

and therefore

    std::cout << n << std::endl;

is equivalent to

    std::cout << 5 << std::endl;

regardless of the definition of f (which may be unknown for compiler).=20
There is no legal way to modify reference x.ref after its initialization so=
=20
that it would refer to a different location. Even if we overwrite the=20
storage of x by construction of a new object of type X at address &x (our=
=20
mysterious f could do such thing), a compiler may assume that reference=20
x.ref is untouched.

The same applies to the original example with optional: it looks like a=20
compiler is free to assume that opt->ref or opt2.storage_.value_.ref still=
=20
refers to n1 (as if the accessed ref would be member of the old object)=20
rather than n2 (to which new ref is supposed to refer). Such a behavior may=
=20
be unexpected for some programmers.

There are several ways to handle the issue. It's possible to:

1) reflect the limitations of the suggested implementation in the=20
specification of std::optional;

2) find a reliable portable (and potentially less effective) implementation=
=20
without limitations regarding to members of reference/const-qualified types=
;

3) acknowledge that an effective implementation of std::optional should=20
rely on some compiler-specific behavior in order to avoid troubles with=20
members of reference/const-qualified types;

4) make two templates: std::optional (which can use std::aligned_storage)=
=20
with normal support of assignment but without support of constexpr=20
semantics, and std::literal_optional (which can use unions in order to=20
implement constexpr semantics);

5) introduce special rules for union members in order to make such tricks=
=20
with unions well-defined;

6) reconsider the existing core rules in 3.8/7 more widely (not only with=
=20
regard to unions).

There may be other options. Which direction is the most preferable?

--=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/?hl=3Den.



------=_Part_41_24142763.1370004491584
Content-Type: text/html; charset=windows-1252
Content-Transfer-Encoding: quoted-printable

[This thread is based on the recent discussion "Implementation of assignmen=
t in std::optional and Core issue 1404: Object reallocation in unions" on S=
G12 reflector (about undefined/unspecified behavior)].<br><br>-------------=
---------------------------------------------------------<br><br>The follow=
ing concerns are related to the suggested implementation of optional - see<=
br>http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3672.html<br>ht=
tps://github.com/akrzemi1/Optional/blob/master/optional.hpp<br><br>Consider=
 the following example:<br><br><span style=3D"font-family: courier new,mono=
space;">&nbsp;&nbsp;&nbsp; #include "optional.hpp"<br>&nbsp;&nbsp;&nbsp; #i=
nclude &lt;iostream&gt;<br><br>&nbsp;&nbsp;&nbsp; struct A<br>&nbsp;&nbsp;&=
nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constexpr A(int &amp;=
x) : ref(x) {}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int &amp;ref;<=
br>&nbsp;&nbsp;&nbsp; };<br><br>&nbsp;&nbsp;&nbsp; int main()<br>&nbsp;&nbs=
p;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int n1 =3D 0, n2 =
=3D 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::experimental::opt=
ional&lt;A&gt; opt =3D A(n1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=
 opt.emplace(n2);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; opt-&gt;ref=
 =3D 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; n1=
 &lt;&lt; " " &lt;&lt; n2 &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; }</span=
><br><br>Here initialization of variable opt implies initialization of unio=
n member <span style=3D"font-family: courier new,monospace;">storage_.value=
_</span> (which has type A). Then expression <span style=3D"font-family: co=
urier new,monospace;">opt.emplace(n2)</span> destroys object <span style=3D=
"font-family: courier new,monospace;">storage_.value_</span> via explicit d=
estructor call and creates new object by placement form of new-expression (=
using forwarded n2 in the new-initializer). All public functions that provi=
de access to the stored value (operator-&gt;(), operator *(), value(), etc.=
), obtain pointer/reference through object expression <span style=3D"font-f=
amily: courier new,monospace;">storage_.value_</span>.<br><br>This is a sim=
plified version of the above code:<br><br><span style=3D"font-family: couri=
er new,monospace;">&nbsp;&nbsp;&nbsp; #include &lt;iostream&gt;<br><br>&nbs=
p;&nbsp;&nbsp; #define FORWARD(x) static_cast&lt;decltype(x) &amp;&amp;&gt;=
(x)<br><br>&nbsp;&nbsp;&nbsp; template &lt;class T&gt;<br>&nbsp;&nbsp;&nbsp=
;&nbsp;&nbsp;&nbsp;&nbsp; union U<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&n=
bsp;&nbsp;&nbsp;&nbsp;&nbsp; constexpr U(T &amp;&amp;x) : value_(FORWARD(x)=
) {}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned char dummy_;<br=
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T value_;<br>&nbsp;&nbsp;&nbsp;=
 };<br><br>&nbsp;&nbsp;&nbsp; template &lt;class T&gt;<br>&nbsp;&nbsp;&nbsp=
;&nbsp;&nbsp;&nbsp;&nbsp; struct optional<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constexpr optional(T &amp;&amp;x) : st=
orage_(FORWARD(x)) {}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; templat=
e &lt;class... Params&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbs=
p;&nbsp;&nbsp;&nbsp; void emplace(Params &amp;&amp;... params)<br>&nbsp;&nb=
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; storage_.value_.~T();<br>&nbsp;&nbsp;&nbsp;&=
nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new (&amp;storage_.value_) =
T(FORWARD(params)...);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><=
br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; U&lt;T&gt; storage_;<br>&nbsp=
;&nbsp;&nbsp; };<br><br>&nbsp;&nbsp;&nbsp; struct A<br>&nbsp;&nbsp;&nbsp; {=
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constexpr A(int &amp;x) : re=
f(x) {}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int &amp;ref;<br>&nbs=
p;&nbsp;&nbsp; };<br><br>&nbsp;&nbsp;&nbsp; int main()<br>&nbsp;&nbsp;&nbsp=
; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int n1 =3D 0, n2 =3D 0;<b=
r>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; optional&lt;A&gt; opt2 =3D A(n=
1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; opt2.emplace(n2);<br>&nbs=
p;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; opt2.storage_.value_.ref =3D 1;<br>&=
nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; n1 &lt;&lt; " =
" &lt;&lt; n2 &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; }</span><br><br>The=
 question is: What may happen at line<br><br><span style=3D"font-family: co=
urier new,monospace;">&nbsp;&nbsp;&nbsp; opt-&gt;ref =3D 1;</span><br><br>i=
n the former code or<br><br><span style=3D"font-family: courier new,monospa=
ce;">&nbsp;&nbsp;&nbsp; opt2.storage_.value_.ref =3D 1;</span><br><br>in th=
e latter (simplified) code?<br><br>According to N3485 - 3.8/7,<br><br>&nbsp=
;&nbsp;&nbsp; If, after the lifetime of an object has ended and before the<=
br>&nbsp;&nbsp;&nbsp; storage which the object occupied is reused or releas=
ed, a new<br>&nbsp;&nbsp;&nbsp; object is created at the storage location w=
hich the original<br>&nbsp;&nbsp;&nbsp; object occupied, a pointer that poi=
nted to the original object, a<br>&nbsp;&nbsp;&nbsp; reference that referre=
d to the original object, or the name of the<br>&nbsp;&nbsp;&nbsp; original=
 object will automatically refer to the new object and,<br>&nbsp;&nbsp;&nbs=
p; once the lifetime of the new object has started, can be used to<br>&nbsp=
;&nbsp;&nbsp; manipulate the new object, if:<br><br>&nbsp;&nbsp;&nbsp; [...=
]<br>&nbsp;&nbsp;&nbsp; =97 the type of the original object is not const-qu=
alified, and, if<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a class type, does not c=
ontain any non-static data member whose<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t=
ype is const-qualified or a reference type, and<br>&nbsp;&nbsp;&nbsp; [...]=
<br><br>In our case the cited condition is not satisfied, because A has a n=
on-static data member of a reference type =97 ref.<br><br>I presume that th=
e intention behind 3.8/7 was to allow optimizations described below:<br><br=
><span style=3D"font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; #in=
clude &lt;iostream&gt;<br><br>&nbsp;&nbsp;&nbsp; struct X<br>&nbsp;&nbsp;&n=
bsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int &amp;ref;<br>&nbsp=
;&nbsp;&nbsp; };<br><br>&nbsp;&nbsp;&nbsp; void f(X &amp;);<br><br>&nbsp;&n=
bsp;&nbsp; int main()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&n=
bsp;&nbsp;&nbsp; int n =3D 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=
 X x{n};<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; f(x);<br>&nbsp;&nbsp=
;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x.ref =3D 5;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nb=
sp;&nbsp;&nbsp; std::cout &lt;&lt; n &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nb=
sp; }<br></span><br>Here a compiler is allowed to assume that<br><br><span =
style=3D"font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; x.ref =3D =
5;</span><br><br>is equivalent to<br><br><span style=3D"font-family: courie=
r new,monospace;">&nbsp;&nbsp;&nbsp; n =3D 5;</span><br><br>and therefore<b=
r><br><span style=3D"font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp=
; std::cout &lt;&lt; n &lt;&lt; std::endl;</span><br><br>is equivalent to<b=
r><br><span style=3D"font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp=
; std::cout &lt;&lt; 5 &lt;&lt; std::endl;</span><br><br>regardless of the =
definition of f (which may be unknown for compiler). There is no legal way =
to modify reference <span style=3D"font-family: courier new,monospace;">x.r=
ef</span> after its initialization so that it would refer to a different lo=
cation. Even if we overwrite the storage of x by construction of a new obje=
ct of type X at address &amp;x (our mysterious f could do such thing), a co=
mpiler may assume that reference <span style=3D"font-family: courier new,mo=
nospace;">x.ref</span> is untouched.<br><br>The same applies to the origina=
l example with optional: it looks like a compiler is free to assume that <s=
pan style=3D"font-family: courier new,monospace;">opt-&gt;ref</span> or <sp=
an style=3D"font-family: courier new,monospace;">opt2.storage_.value_.ref</=
span> still refers to n1 (as if the accessed <span style=3D"font-family: co=
urier new,monospace;">ref</span> would be member of the old object) rather =
than n2 (to which new <span style=3D"font-family: courier new,monospace;">r=
ef</span> is supposed to refer). Such a behavior may be unexpected for some=
 programmers.<br><br>There are several ways to handle the issue. It's possi=
ble to:<br><br>1) reflect the limitations of the suggested implementation i=
n the specification of std::optional;<br><br>2) find a reliable portable (a=
nd potentially less effective) implementation without limitations regarding=
 to members of reference/const-qualified types;<br><br>3) acknowledge that =
an effective implementation of std::optional should rely on some compiler-s=
pecific behavior in order to avoid troubles with members of reference/const=
-qualified types;<br><br>4) make two templates: std::optional (which can us=
e std::aligned_storage) with normal support of assignment but without suppo=
rt of constexpr semantics, and std::literal_optional (which can use unions =
in order to implement constexpr semantics);<br><br>5) introduce special rul=
es for union members in order to make such tricks with unions well-defined;=
<br><br>6) reconsider the existing core rules in 3.8/7 more widely (not onl=
y with regard to unions).<br><br>There may be other options. Which directio=
n is the most preferable?<br>

<p></p>

-- <br />
&nbsp;<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/?hl=3Den">http://groups.google.com/a/isocpp.org/group/std-pro=
posals/?hl=3Den</a>.<br />
&nbsp;<br />
&nbsp;<br />

------=_Part_41_24142763.1370004491584--

.
