Topic: Contra P0722R0 "destroying operator-delete
Author: Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
Date: Tue, 17 Oct 2017 09:39:03 -0700 (PDT)
Raw View
------=_Part_19517_1569868226.1508258343528
Content-Type: multipart/alternative;
boundary="----=_Part_19518_1262803990.1508258343528"
------=_Part_19518_1262803990.1508258343528
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
This is related to Richard Smith and Andrew Hunter's P0722R0 "Controlling=
=20
destruction in delete expressions"=20
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0722r0.htm>.
The paper begins this way:
Consider the following class:
class inlined_fixed_string {
public:
inlined_fixed_string() =3D delete;
const size_t size() const { return size_; }
const char *data() const {
return static_cast<const char *>(this + 1);
}
// operator[], etc, with obvious implementations
inlined_fixed_string *Make(const std::string &data) {
size_t full_size =3D sizeof(inlined_fixed_string) + data.size();
return new(::operator new(full_size))
inlined_fixed_string(data.size(), data.c_str());
}
private:
inlined_fixed_string(size_t size, const char *data) : size_(size) {
memcpy(data(), data, size);
}
size_t size_;
};
This defines what is (effectively) a variable-sized object, using that to
implement an array of size determined at runtime while saving a pointer
indirection. (Note: this pattern is simpler (and generally written) with
flexible array members, despite their being nonstandard.)
However, what happens when we delete such a string s in the presence of
sized-delete?
Well, you get misbehavior, of course. Syntactically, C++ allows you to use=
=20
"delete" on any pointer; but semantically, you should use "delete" only on=
=20
pointers that were originally obtained from "new". And in your case, you=20
didn't obtain the pointer from "new"; you obtained it from the public=20
factory function "Make".
This code is broken because it has a public "Make" factory and private=20
constructor, but it is missing a public "Destroy" factory and private=20
destructor. If you rewrite it to use that idiom, then the problem goes away=
..
The corrected code looks like this=20
<https://wandbox.org/permlink/YMYEwUFZtLlHeHEo>, and requires absolutely no=
=20
core language changes.
class inlined_fixed_string {
public:
inlined_fixed_string() =3D delete;
size_t size() const { return size_; }
char *data() {
return reinterpret_cast<char *>(this + 1);
}
// operator[], etc, with obvious implementations
static inlined_fixed_string *Make(const std::string &data) {
size_t full_size =3D sizeof(inlined_fixed_string) + data.size();
return new(::operator new(full_size))
inlined_fixed_string(data.size(), data.c_str());
}
static void Destroy(inlined_fixed_string *p) {
size_t full_size =3D sizeof(*p) + p->size();
p->~inlined_fixed_string();
::operator delete(p, full_size);
}
private:
inlined_fixed_string(size_t n, const char *s) : size_(n) {
memcpy(data(), s, n);
}
~inlined_fixed_string() {}
size_t size_;
};
int main() {
inlined_fixed_string *s =3D inlined_fixed_string::Make("hello world");
inlined_fixed_string::Destroy(s);
std::shared_ptr<inlined_fixed_string> p(inlined_fixed_string::Make("hel=
lo shared"), inlined_fixed_string::Destroy);
}
That said, if you do pursue "destroy and delete in one atomic operation",=
=20
it will be of especial interest to the garbage-collection and RCU folks.=20
Louis Dionne is interested in building a deferred_reclamation_allocator<T>=
=20
that can defer calls to destroy() and deallocate() in pairs, which is=20
essentially the primitive you wanted to provide in P0722R0. (But again, you=
=20
don't need P0722R0, and I'd much much rather the Committee not pursue it.=
=20
Nobody understands new/delete as it is; let's not make the situation even=
=20
worse out of a misbegotten wish to "delete" objects we haven't "new"ed.)=20
Incidentally, if the individual words didn't already have domain-specific=
=20
meanings, I would love to describe inlined_fixed_string's semantics in=20
terms of the Make "factory function" and the Destroy "glue factory=20
function". ;)
my $.02,
=E2=80=93Arthur
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/89c7d198-0eec-4099-ba31-bad92706ebae%40isocpp.or=
g.
------=_Part_19518_1262803990.1508258343528
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">This is related to Richard Smith and Andrew Hunter's <=
a href=3D"http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0722r0.h=
tm">P0722R0 "Controlling destruction in delete expressions"</a>.<=
br>The paper begins this way:<div><br></div><div><pre style=3D"color: rgb(0=
, 0, 0);">Consider the following class:
class inlined_fixed_string {
public:
inlined_fixed_string() =3D delete;
const size_t size() const { return size_; }
const char *data() const {
return static_cast<const char *>(this + 1);
}
// operator[], etc, with obvious implementations
inlined_fixed_string *Make(const std::string &data) {
size_t full_size =3D sizeof(inlined_fixed_string) + data.size();
return new(::operator new(full_size))
inlined_fixed_string(data.size(), data.c_str());
}
private:
inlined_fixed_string(size_t size, const char *data) : size_(size) {
memcpy(data(), data, size);
}
size_t size_;
};
This defines what is (effectively) a variable-sized object, using that to
implement an array of size determined at runtime while saving a pointer
indirection. (Note: this pattern is simpler (and generally written) with
flexible array members, despite their being nonstandard.)
However, what happens when we delete such a string s in the presence of
sized-delete?</pre><br>Well, you get misbehavior, of course. Syntactically,=
C++ allows you to use "delete" on any pointer; but semantically,=
you should use "delete" only on pointers that were originally ob=
tained from "new". And in your case, you didn't obtain the po=
inter from "new"; you obtained it from the public factory functio=
n "Make".</div><div>This code is broken because it has a public &=
quot;Make" factory and private constructor, but it is missing a public=
"Destroy" factory and private destructor. If you rewrite it to u=
se that idiom, then the problem goes away.</div><div><a href=3D"https://wan=
dbox.org/permlink/YMYEwUFZtLlHeHEo">The corrected code looks like this</a>,=
and requires absolutely no core language changes.</div><div><br></div><div=
><pre style=3D"color: rgb(0, 0, 0);">class inlined_fixed_string {
public:
inlined_fixed_string() =3D delete;
size_t size() const { return size_; }
char *data() {
return reinterpret_cast<char *>(this + 1);
}
// operator[], etc, with obvious implementations
static inlined_fixed_string *Make(const std::string &data) {
size_t full_size =3D sizeof(inlined_fixed_string) + data.size();
return new(::operator new(full_size))
inlined_fixed_string(data.size(), data.c_str());
}
static void Destroy(inlined_fixed_string *p) {
size_t full_size =3D sizeof(*p) + p->size();
p->~inlined_fixed_string();
::operator delete(p, full_size);
}
private:
inlined_fixed_string(size_t n, const char *s) : size_(n) {
memcpy(data(), s, n);
}
~inlined_fixed_string() {}
size_t size_;
};
int main() {
inlined_fixed_string *s =3D inlined_fixed_string::Make("hello worl=
d");
inlined_fixed_string::Destroy(s);
std::shared_ptr<inlined_fixed_string> p(inlined_fixed_string::Mak=
e("hello shared"), inlined_fixed_string::Destroy);
}<br></pre><pre style=3D"color: rgb(0, 0, 0);"><br></pre><pre style=3D"colo=
r: rgb(0, 0, 0);"><span style=3D"color: rgb(34, 34, 34); font-family: Arial=
, Helvetica, sans-serif;"><br></span></pre>That said, if you do pursue &quo=
t;destroy and delete in one atomic operation", it will be of especial =
interest to the garbage-collection and RCU folks. Louis Dionne is intereste=
d in building a deferred_reclamation_allocator<T> that can defer call=
s to destroy() and deallocate() in pairs, which is essentially the primitiv=
e you wanted to provide in P0722R0. (But again, you don't need P0722R0,=
and I'd much much rather the Committee not pursue it. Nobody understan=
ds new/delete as it is; let's not make the situation even worse out of =
a misbegotten wish to "delete" objects we haven't "new&q=
uot;ed.) <br><br>Incidentally, if the individual words didn't already h=
ave domain-specific meanings, I would love to describe inlined_fixed_string=
's semantics in terms of the Make "factory function" and the =
Destroy "glue factory function". ;)<br><br>my $.02,<br>=E2=80=93A=
rthur</div></div>
<p></p>
-- <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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/89c7d198-0eec-4099-ba31-bad92706ebae%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/89c7d198-0eec-4099-ba31-bad92706ebae=
%40isocpp.org</a>.<br />
------=_Part_19518_1262803990.1508258343528--
------=_Part_19517_1569868226.1508258343528--
.
Author: "'Richard Smith' via ISO C++ Standard - Future Proposals" <std-proposals@isocpp.org>
Date: Tue, 17 Oct 2017 17:02:11 +0000
Raw View
--001a11476de036456d055bc116e1
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
On Tue, 17 Oct 2017, 09:39 Arthur O'Dwyer, <arthur.j.odwyer@gmail.com>
wrote:
> This is related to Richard Smith and Andrew Hunter's P0722R0 "Controlling
> destruction in delete expressions"
> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0722r0.htm>.
> The paper begins this way:
>
> Consider the following class:
>
> class inlined_fixed_string {
> public:
> inlined_fixed_string() =3D delete;
> const size_t size() const { return size_; }
>
> const char *data() const {
> return static_cast<const char *>(this + 1);
> }
>
> // operator[], etc, with obvious implementations
>
> inlined_fixed_string *Make(const std::string &data) {
> size_t full_size =3D sizeof(inlined_fixed_string) + data.size();
> return new(::operator new(full_size))
> inlined_fixed_string(data.size(), data.c_str());
> }
>
> private:
> inlined_fixed_string(size_t size, const char *data) : size_(size) {
> memcpy(data(), data, size);
> }
> size_t size_;
> };
>
> This defines what is (effectively) a variable-sized object, using that to
> implement an array of size determined at runtime while saving a pointer
> indirection. (Note: this pattern is simpler (and generally written) with
> flexible array members, despite their being nonstandard.)
>
> However, what happens when we delete such a string s in the presence of
> sized-delete?
>
>
> Well, you get misbehavior, of course. Syntactically, C++ allows you to us=
e
> "delete" on any pointer; but semantically, you should use "delete" only o=
n
> pointers that were originally obtained from "new". And in your case, you
> didn't obtain the pointer from "new"; you obtained it from the public
> factory function "Make".
> This code is broken because it has a public "Make" factory and private
> constructor, but it is missing a public "Destroy" factory and private
> destructor. If you rewrite it to use that idiom, then the problem goes aw=
ay.
>
Please read to the end of the paper; that alternative is discussed and we
explain why it is not entirely satisfactory.
The corrected code looks like this
> <https://wandbox.org/permlink/YMYEwUFZtLlHeHEo>, and requires absolutely
> no core language changes.
>
> class inlined_fixed_string {
> public:
> inlined_fixed_string() =3D delete;
> size_t size() const { return size_; }
>
> char *data() {
> return reinterpret_cast<char *>(this + 1);
> }
>
> // operator[], etc, with obvious implementations
>
> static inlined_fixed_string *Make(const std::string &data) {
> size_t full_size =3D sizeof(inlined_fixed_string) + data.size();
> return new(::operator new(full_size))
> inlined_fixed_string(data.size(), data.c_str());
> }
>
> static void Destroy(inlined_fixed_string *p) {
> size_t full_size =3D sizeof(*p) + p->size();
> p->~inlined_fixed_string();
> ::operator delete(p, full_size);
> }
> private:
> inlined_fixed_string(size_t n, const char *s) : size_(n) {
> memcpy(data(), s, n);
> }
> ~inlined_fixed_string() {}
> size_t size_;
> };
>
> int main() {
> inlined_fixed_string *s =3D inlined_fixed_string::Make("hello world")=
;
> inlined_fixed_string::Destroy(s);
>
> std::shared_ptr<inlined_fixed_string> p(inlined_fixed_string::Make("h=
ello shared"), inlined_fixed_string::Destroy);
> }
>
>
>
> That said, if you do pursue "destroy and delete in one atomic operation",
> it will be of especial interest to the garbage-collection and RCU folks.
> Louis Dionne is interested in building a deferred_reclamation_allocator<T=
>
> that can defer calls to destroy() and deallocate() in pairs, which is
> essentially the primitive you wanted to provide in P0722R0. (But again, y=
ou
> don't need P0722R0, and I'd much much rather the Committee not pursue it.
> Nobody understands new/delete as it is; let's not make the situation even
> worse out of a misbegotten wish to "delete" objects we haven't "new"ed.)
>
> Incidentally, if the individual words didn't already have domain-specific
> meanings, I would love to describe inlined_fixed_string's semantics in
> terms of the Make "factory function" and the Destroy "glue factory
> function". ;)
>
> my $.02,
> =E2=80=93Arthur
>
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/CAGL0aWf-x-A5YaYoQYQw%2BxdokBHxGJ4MgZkc0%2B-B%2B=
%2B5Da%2BFpOQ%40mail.gmail.com.
--001a11476de036456d055bc116e1
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"auto"><div><div class=3D"gmail_quote"><div dir=3D"ltr">On Tue, =
17 Oct 2017, 09:39 Arthur O'Dwyer, <<a href=3D"mailto:arthur.j.odwye=
r@gmail.com">arthur.j.odwyer@gmail.com</a>> wrote:<br></div><blockquote =
class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1px #ccc solid=
;padding-left:1ex"><div dir=3D"ltr">This is related to Richard Smith and An=
drew Hunter's <a href=3D"http://www.open-std.org/jtc1/sc22/wg21/docs/pa=
pers/2017/p0722r0.htm" target=3D"_blank">P0722R0 "Controlling destruct=
ion in delete expressions"</a>.<br>The paper begins this way:<div><br>=
</div><div><pre style=3D"color:rgb(0,0,0)">Consider the following class:
class inlined_fixed_string {
public:
inlined_fixed_string() =3D delete;
const size_t size() const { return size_; }
const char *data() const {
return static_cast<const char *>(this + 1);
}
// operator[], etc, with obvious implementations
inlined_fixed_string *Make(const std::string &data) {
size_t full_size =3D sizeof(inlined_fixed_string) + data.size();
return new(::operator new(full_size))
inlined_fixed_string(data.size(), data.c_str());
}
private:
inlined_fixed_string(size_t size, const char *data) : size_(size) {
memcpy(data(), data, size);
}
size_t size_;
};
This defines what is (effectively) a variable-sized object, using that to
implement an array of size determined at runtime while saving a pointer
indirection. (Note: this pattern is simpler (and generally written) with
flexible array members, despite their being nonstandard.)
However, what happens when we delete such a string s in the presence of
sized-delete?</pre><br>Well, you get misbehavior, of course. Syntactically,=
C++ allows you to use "delete" on any pointer; but semantically,=
you should use "delete" only on pointers that were originally ob=
tained from "new". And in your case, you didn't obtain the po=
inter from "new"; you obtained it from the public factory functio=
n "Make".</div><div>This code is broken because it has a public &=
quot;Make" factory and private constructor, but it is missing a public=
"Destroy" factory and private destructor. If you rewrite it to u=
se that idiom, then the problem goes away.</div></div></blockquote></div></=
div><div dir=3D"auto"><br></div><div dir=3D"auto">Please read to the end of=
the paper; that alternative is discussed and we explain why it is not enti=
rely satisfactory.</div><div dir=3D"auto"><br></div><div dir=3D"auto"><div =
class=3D"gmail_quote"><blockquote class=3D"gmail_quote" style=3D"margin:0 0=
0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div>=
<a href=3D"https://wandbox.org/permlink/YMYEwUFZtLlHeHEo" target=3D"_blank"=
>The corrected code looks like this</a>, and requires absolutely no core la=
nguage changes.</div><div><br></div><div><pre style=3D"color:rgb(0,0,0)">cl=
ass inlined_fixed_string {
public:
inlined_fixed_string() =3D delete;
size_t size() const { return size_; }
char *data() {
return reinterpret_cast<char *>(this + 1);
}
// operator[], etc, with obvious implementations
static inlined_fixed_string *Make(const std::string &data) {
size_t full_size =3D sizeof(inlined_fixed_string) + data.size();
return new(::operator new(full_size))
inlined_fixed_string(data.size(), data.c_str());
}
static void Destroy(inlined_fixed_string *p) {
size_t full_size =3D sizeof(*p) + p->size();
p->~inlined_fixed_string();
::operator delete(p, full_size);
}
private:
inlined_fixed_string(size_t n, const char *s) : size_(n) {
memcpy(data(), s, n);
}
~inlined_fixed_string() {}
size_t size_;
};
int main() {
inlined_fixed_string *s =3D inlined_fixed_string::Make("hello worl=
d");
inlined_fixed_string::Destroy(s);
std::shared_ptr<inlined_fixed_string> p(inlined_fixed_string::Mak=
e("hello shared"), inlined_fixed_string::Destroy);
}<br></pre><pre style=3D"color:rgb(0,0,0)"><br></pre><pre style=3D"color:rg=
b(0,0,0)"><span style=3D"color:rgb(34,34,34);font-family:Arial,Helvetica,sa=
ns-serif"><br></span></pre>That said, if you do pursue "destroy and de=
lete in one atomic operation", it will be of especial interest to the =
garbage-collection and RCU folks. Louis Dionne is interested in building a =
deferred_reclamation_allocator<T> that can defer calls to destroy() a=
nd deallocate() in pairs, which is essentially the primitive you wanted to =
provide in P0722R0. (But again, you don't need P0722R0, and I'd muc=
h much rather the Committee not pursue it. Nobody understands new/delete as=
it is; let's not make the situation even worse out of a misbegotten wi=
sh to "delete" objects we haven't "new"ed.) <br><br=
>Incidentally, if the individual words didn't already have domain-speci=
fic meanings, I would love to describe inlined_fixed_string's semantics=
in terms of the Make "factory function" and the Destroy "gl=
ue factory function". ;)<br><br>my $.02,<br>=E2=80=93Arthur</div></div=
></blockquote></div></div></div>
<p></p>
-- <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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/CAGL0aWf-x-A5YaYoQYQw%2BxdokBHxGJ4MgZ=
kc0%2B-B%2B%2B5Da%2BFpOQ%40mail.gmail.com?utm_medium=3Demail&utm_source=3Df=
ooter">https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGL0aW=
f-x-A5YaYoQYQw%2BxdokBHxGJ4MgZkc0%2B-B%2B%2B5Da%2BFpOQ%40mail.gmail.com</a>=
..<br />
--001a11476de036456d055bc116e1--
.
Author: Thiago Macieira <thiago@macieira.org>
Date: Tue, 17 Oct 2017 11:03:55 -0700
Raw View
On ter=C3=A7a-feira, 17 de outubro de 2017 10:02:11 PDT 'Richard Smith' via=
ISO C++=20
Standard - Future Proposals wrote:
> Please read to the end of the paper; that alternative is discussed and we
> explain why it is not entirely satisfactory.
And I agree. My first reaction was, "why not just add a class operator dele=
te",=20
which is answered in the paper.
I like the idea of the destroying operator delete.
Can we have a constructing operator new too? That is, the dual of what the=
=20
paper proposes. That would allow for the object to have a public constructo=
r=20
too. This has been a request in QObject for some time, but we have never fo=
und=20
a satisfactory (i.e., non-hacky or inefficient) solution.
Almost all class types using private implementations would benefit from thi=
s.=20
Take this from actual code <https://code.woboq.org/qt5/qtbase/src/corelib/
kernel/qobject.cpp.html#_ZN7QObjectC1EPS_>:
QObject::QObject(QObject *parent)
: d_ptr(new QObjectPrivate)
You'll find that QObject is roughly even divided between stack / member obj=
ects=20
and those created with new QObject (or one of the derived classes).
It would be grand to have:
QObject *QObject::operator new(/* implicit size */)
{
size_t full_size =3D sizeof(QObject) + sizeof(QObjectPrivate);
auto obj =3D static_cast<QObject *>(::operator new(full_size));
auto d =3D reinterpret_cast<QObjectPrivate *>(obj + 1);
// call a private or protected constructor
return new (obj) QObject(InPlaceTag{}, d);
}
Coupled with a member operator delete (not necessarily the destroying delet=
e),=20
the only other things we need is 1 bit indicating that the main class does =
not=20
own the private object and should not delete it.
--=20
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/1643738.UGnyhZm5To%40tjmaciei-mobl1.
.
Author: "Arthur O'Dwyer" <arthur.j.odwyer@gmail.com>
Date: Tue, 17 Oct 2017 11:30:09 -0700
Raw View
--94eb2c19516c30efc0055bc25084
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
On Tue, Oct 17, 2017 at 10:02 AM, Richard Smith <richardsmith@google.com>
wrote:
> On Tue, 17 Oct 2017, 09:39 Arthur O'Dwyer, <arthur.j.odwyer@gmail.com>
> wrote:
>
>> This defines what is (effectively) a variable-sized object, using that t=
o
>> implement an array of size determined at runtime while saving a pointer
>> indirection. (Note: this pattern is simpler (and generally written) with
>> flexible array members, despite their being nonstandard.)
>>
>> However, what happens when we delete such a string s in the presence of
>> sized-delete?
>>
>>
>> Well, you get misbehavior, of course. Syntactically, C++ allows you to
>> use "delete" on any pointer; but semantically, you should use "delete" o=
nly
>> on pointers that were originally obtained from "new". And in your case, =
you
>> didn't obtain the pointer from "new"; you obtained it from the public
>> factory function "Make".
>> This code is broken because it has a public "Make" factory and private
>> constructor, but it is missing a public "Destroy" factory and private
>> destructor. If you rewrite it to use that idiom, then the problem goes a=
way.
>>
>
> Please read to the end of the paper; that alternative is discussed and we
> explain why it is not entirely satisfactory.
>
I saw the end of the paper, but I interpreted it as just a continuation of
the mistaken idea that it's okay to "delete" an object that was not created
with "new". P0722R0 ends like this:
The obvious and natural alternative to this proposal is to destroy and dele=
te
objects with custom deletion semantics by using special-case logic, instead
of using 'operator delete'. This has a number of disadvantages:
* It violates the principle that user-defined types are used like built-in
types: as soon as you use more advanced allocation techniques, you don't
get to use delete expressions any more.
Yes, this is accurate. The programmer should never expect "delete
expressions" to work with objects that were not created via "new
expressions". The programmer should always expect that if a pointer is
gotten from a "factory", then it should be returned to the corresponding
"glue factory" when the programmer is done with it. You can use RAII to
ensure that this happens. One built-in RAII approach is to use
std::shared_ptr as in my corrected example
<https://wandbox.org/permlink/YMYEwUFZtLlHeHEo>. Another approach is to
have the "factory" return a value-semantic wrapper around the polymorphic
object, in which case you have exactly type-erasure =C3=A1 l=C3=A0 std::fun=
ction.
*Sometimes* the appropriate "glue factory" is "delete p", but it should be
unsurprising that sometimes the appropriate "glue factory" is *not* "delete
p". As soon as your "allocation technique" becomes more advanced than "p =
=3D
new T(...)", you should expect that your "deallocation technique" will have
to be more advanced than "delete p".
* Existing class hierarchies with virtual destructors cannot be transparen=
tly
extended with derived classes that allocate dynamic leading or trailing
storage.
Mmmm, I'd say yes they can (although it's not a good idea). If the existing
class hierarchy provides a "factory" function for creating objects of the
polymorphic type, then it would make sense for the derived class to try to
"override" that factory function, and to override the corresponding "glue
factory" function for destruction-and-deallocation. If the class author
intended for the "glue factory" function to be overridden, they'll have
made it a virtual method:
virtual void Destroy() {
inlined_fixed_string *p =3D this;
size_t full_size =3D sizeof(*p) + p->size();
p->~inlined_fixed_string();
::operator delete(p, full_size);
}
However, this is fundamentally different from a destructor alone. A derived
class's destructor implicitly calls its base's destructor (there's no way
around that in C++ today); but a derived class's Destroy() *must not* call
its base's Destroy(). This suggests that the derived class must *itself*
know everything about how to clean up its base; which means that this is
*probably* not a good fit with classical OOP.
Can you imagine a derived class extending inlined_fixed_string? How would
it look and act? Especially, how would I construct and destroy instances
of the derived class?
* The local choice of deallocation strategy leaks out to clients of the co=
de.
For example, a custom deleter must be specified when using unique_ptr<T>=
,
and make_unique and make_shared can't be used any more.
Note that the standard requires specializations of default_delete<T> to hav=
e
the same effect as calling "delete p;", so specializing default_delete is n=
ot
a correct alternative in C++17. We could lift that restriction, but that wo=
uld
not help for other (perhaps user-defined) resource management types that us=
e
new and delete to manage objects.
The last bullet and its subsequent paragraph seem to hold two competing
views of how-and-whether to extend C++. The bullet says, "We want to use
'delete' on factory-made pointers and have it Just Work. We must change the
language to make that syntax work!" But the paragraph says, "You want to
use 'default_delete' on factory-made pointers and have it Just Work? No,
that would just complicate the language."
Whereas my attitude is "You want to use *anything other than the
corresponding factory method* to deallocate factory-made pointers and
expect it to work? Just say no."
=E2=80=93Arthur
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/CADvuK0JauVfSmsCMLgvdhwZmGkpYrx%2BnYh4rQc64%2Bx9=
YaG5u1g%40mail.gmail.com.
--94eb2c19516c30efc0055bc25084
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Tue, Oct 17, 2017 at 10:02 AM, Richard Smith <span dir=
=3D"ltr"><<a href=3D"mailto:richardsmith@google.com" target=3D"_blank">r=
ichardsmith@google.com</a>></span> wrote:<br><div class=3D"gmail_extra">=
<div class=3D"gmail_quote"><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"auto"><div><div cla=
ss=3D"gmail-h5"><div><div class=3D"gmail_quote"><div dir=3D"ltr">On Tue, 17=
Oct 2017, 09:39 Arthur O'Dwyer, <<a href=3D"mailto:arthur.j.odwyer@=
gmail.com" target=3D"_blank">arthur.j.odwyer@gmail.com</a>> wrote:</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;p=
adding-left:1ex"><div dir=3D"ltr"><div><pre style=3D"color:rgb(0,0,0)">This=
defines what is (effectively) a variable-sized object, using that to
implement an array of size determined at runtime while saving a pointer
indirection. (Note: this pattern is simpler (and generally written) with
flexible array members, despite their being nonstandard.)
However, what happens when we delete such a string s in the presence of
sized-delete?</pre><br>Well, you get misbehavior, of course. Syntactically,=
C++ allows you to use "delete" on any pointer; but semantically,=
you should use "delete" only on pointers that were originally ob=
tained from "new". And in your case, you didn't obtain the po=
inter from "new"; you obtained it from the public factory functio=
n "Make".</div><div>This code is broken because it has a public &=
quot;Make" factory and private constructor, but it is missing a public=
"Destroy" factory and private destructor. If you rewrite it to u=
se that idiom, then the problem goes away.</div></div></blockquote></div></=
div><div dir=3D"auto"><br></div></div></div><div dir=3D"auto">Please read t=
o the end of the paper; that alternative is discussed and we explain why it=
is not entirely satisfactory.</div></div></blockquote><div><br></div><div>=
I saw the end of the paper, but I interpreted it as just a continuation of =
the mistaken idea that it's okay to "delete" an object that w=
as not created with "new". P0722R0 ends like this:</div><div><br>=
</div><div><pre style=3D"color:rgb(0,0,0)">The obvious and natural alternat=
ive to this proposal is to destroy and delete
objects with custom deletion semantics by using special-case logic, instead
of using 'operator delete'. This has a number of disadvantages:
* It violates the principle that user-defined types are used like built-in
types: as soon as you use more advanced allocation techniques, you don&#=
39;t
get to use delete expressions any more.
<br></pre>Yes, this is accurate. The programmer should never expect "d=
elete expressions" to work with objects that were not created via &quo=
t;new expressions".=C2=A0 The programmer should always expect that if =
a pointer is gotten from a "factory", then it should be returned =
to the corresponding "glue factory" when the programmer is done w=
ith it. You can use RAII to ensure that this happens. One built-in RAII app=
roach is to use std::shared_ptr as in <a href=3D"https://wandbox.org/permli=
nk/YMYEwUFZtLlHeHEo">my corrected example</a>.=C2=A0 Another approach is to=
have the "factory" return a value-semantic wrapper around the po=
lymorphic object, in which case you have exactly type-erasure =C3=A1 l=C3=
=A0 std::function.</div><div><i>Sometimes</i> the appropriate "glue fa=
ctory" is "delete p", but it should be unsurprising that som=
etimes the appropriate "glue factory" is <i>not</i> "delete =
p". As soon as your "allocation technique" becomes more adva=
nced than "p =3D new T(...)", you should expect that your "d=
eallocation technique" will have to be more advanced than "delete=
p".</div><div><br></div><div><br><pre><font color=3D"#000000"> * Exis=
ting class hierarchies with virtual destructors cannot be transparently
extended with derived classes that allocate dynamic leading or trailing
storage.</font><br><br></pre>Mmmm, I'd say yes they can (although it=
's not a good idea). If the existing class hierarchy provides a "f=
actory" function for creating objects of the polymorphic type, then it=
would make sense for the derived class to try to "override" that=
factory function, and to override the corresponding "glue factory&quo=
t; function for destruction-and-deallocation. If the class author intended =
for the "glue factory" function to be overridden, they'll hav=
e made it a virtual method:</div><div><br></div><div><div><font face=3D"mon=
ospace, monospace">=C2=A0 =C2=A0virtual void Destroy() {</font></div><div><=
font face=3D"monospace, monospace">=C2=A0 =C2=A0 =C2=A0inlined_fixed_string=
*p =3D this;</font></div><div><font face=3D"monospace, monospace">=C2=A0 =
=C2=A0 =C2=A0size_t full_size =3D sizeof(*p) + p->size();</font></div><d=
iv><font face=3D"monospace, monospace">=C2=A0 =C2=A0 =C2=A0p->~inlined_f=
ixed_string();</font></div><div><font face=3D"monospace, monospace">=C2=A0 =
=C2=A0 =C2=A0::operator delete(p, full_size);</font></div><div><font face=
=3D"monospace, monospace">=C2=A0 =C2=A0}</font></div></div><div><br></div><=
div>However, this is fundamentally different from a destructor alone. A der=
ived class's destructor implicitly calls its base's destructor (the=
re's no way around that in C++ today); but a derived class's Destro=
y()=C2=A0<b><i>must not</i></b> call its base's Destroy(). This suggest=
s that the derived class must <i>itself</i> know everything about how to cl=
ean up its base; which means that this is <i>probably</i> not a good fit wi=
th classical OOP.</div><div>Can you imagine a derived class extending inlin=
ed_fixed_string?=C2=A0 How would it look and act?=C2=A0 Especially, how wou=
ld I construct and destroy instances of the derived class?</div><div><br><p=
re style=3D"color:rgb(0,0,0)"> * The local choice of deallocation strategy =
leaks out to clients of the code.
For example, a custom deleter must be specified when using unique_ptr<=
;T>,
and make_unique and make_shared can't be used any more.
Note that the standard requires specializations of default_delete<T> =
to have
the same effect as calling "delete p;", so specializing default_d=
elete is not
a correct alternative in C++17. We could lift that restriction, but that wo=
uld
not help for other (perhaps user-defined) resource management types that us=
e
new and delete to manage objects.</pre></div><div>The last bullet and its s=
ubsequent paragraph seem to hold two competing views of how-and-whether to =
extend C++. The bullet says, "We want to use 'delete' on facto=
ry-made pointers and have it Just Work. We must change the language to make=
that syntax work!" =C2=A0But the paragraph says, "You want to us=
e 'default_delete' on factory-made pointers and have it Just Work? =
No, that would just complicate the language."</div><div>Whereas my att=
itude is "You want to use <i>anything other than the corresponding fac=
tory method</i> to deallocate factory-made pointers and expect it to work? =
Just say no."</div><div><br></div><div>=E2=80=93Arthur</div></div></di=
v></div>
<p></p>
-- <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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/CADvuK0JauVfSmsCMLgvdhwZmGkpYrx%2BnYh=
4rQc64%2Bx9YaG5u1g%40mail.gmail.com?utm_medium=3Demail&utm_source=3Dfooter"=
>https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CADvuK0JauVfS=
msCMLgvdhwZmGkpYrx%2BnYh4rQc64%2Bx9YaG5u1g%40mail.gmail.com</a>.<br />
--94eb2c19516c30efc0055bc25084--
.
Author: "'Andrew Hunter' via ISO C++ Standard - Future Proposals" <std-proposals@isocpp.org>
Date: Tue, 17 Oct 2017 12:39:58 -0700
Raw View
On Tue, Oct 17, 2017 at 11:30 AM, Arthur O'Dwyer
<arthur.j.odwyer@gmail.com> wrote:
> Yes, this is accurate. The programmer should never expect "delete
> expressions" to work with objects that were not created via "new
> expressions".
Which programmer?
Suppose I have 10,000 programmers who make use of a particular
string-like class (as a matter of fact, I actually do.) There are only
a very few sources of this object, but plenty of consumers. It
currently uses no hanky-panky in allocation; therefore everyone
happily deletes them, stores them in unique_ptr, and so on. There is
X00 kLOC of code doing this. Now someone realizes that this class, on
a number of hot paths, would benefit from inlining allocation as in
the example. We can either rewrite every line of code, and train
every programmer to only use Destroy, and enforce use of
custom-deleter unique_ptrs, and ban every container that doesn't have
full support for that...or we can make "delete s" do the right thing.
As the paper points out, this is in fact already possible to do;
defining operator delete on the type works and is well-defined. But
it gives up efficiency in a sized-delete world.
> Sometimes the appropriate "glue factory" is "delete p", but it should be
> unsurprising that sometimes the appropriate "glue factory" is not "delete
> p".
I do not think this is an argument against making it possible to have
that deallocation function be "delete p" where we can.
> * The local choice of deallocation strategy leaks out to clients of the
> code.
> For example, a custom deleter must be specified when using unique_ptr<T>,
> and make_unique and make_shared can't be used any more.
>
> Note that the standard requires specializations of default_delete<T> to have
> the same effect as calling "delete p;", so specializing default_delete is
> not
> a correct alternative in C++17. We could lift that restriction, but that
> would
> not help for other (perhaps user-defined) resource management types that use
> new and delete to manage objects.
>
> The last bullet and its subsequent paragraph seem to hold two competing
> views of how-and-whether to extend C++. The bullet says, "We want to use
> 'delete' on factory-made pointers and have it Just Work. We must change the
> language to make that syntax work!" But the paragraph says, "You want to
> use 'default_delete' on factory-made pointers and have it Just Work? No,
> that would just complicate the language."
You may be misreading that section? We are saying that without P0722
or a similar fix, it is not possible to make default_delete work, as
overriding it would violate the spec. And while changing *that* rule,
instead of adding the feature we propose, would solve the problem for
std::unique_ptr<T> specifically, it would do nothing for all the
*other* possible users.
> Whereas my attitude is "You want to use anything other than the
> corresponding factory method to deallocate factory-made pointers and expect
> it to work? Just say no."
>
I think you are perhaps not seeing the proper perspective: we are
trying to widen the already large set of cases where "delete p" *is*
the appropriate factory deletion method. This is already an extremely
common pattern, and one with numerous advantages (the paper specifies
some of them.) Remember in particular that there are many reasons
where factory-creation is a useful pattern without any funny business
happening on the allocation (the trivial example being creation of an
object chosen dynamically from some virtual hierarchy.) Are you
saying that
Widget *Widget::Make(string widget_spec);
is badly designed unless I always also specify Widget::Destroy? (If
so, why do we have virtual destructors at all?) We simply want to be
able to use delete as our "factory deallocation method" in some
additional cases.
--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CADroS%3D7yejukTCXZT8KW-T3bouQZUU-_xxLq42vfZQsmUULP_Q%40mail.gmail.com.
.
Author: "Arthur O'Dwyer" <arthur.j.odwyer@gmail.com>
Date: Tue, 17 Oct 2017 13:06:54 -0700
Raw View
--f403045c14682afdf3055bc3aaf4
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
On Tue, Oct 17, 2017 at 12:39 PM, Andrew Hunter <ahh@google.com> wrote:
> On Tue, Oct 17, 2017 at 11:30 AM, Arthur O'Dwyer
> <arthur.j.odwyer@gmail.com> wrote:
> > Yes, this is accurate. The programmer should never expect "delete
> > expressions" to work with objects that were not created via "new
> > expressions".
>
> Which programmer?
>
All of them. ;)
Are you saying that
>
> Widget *Widget::Make(string widget_spec);
>
> is badly designed unless I always also specify Widget::Destroy?
Yes. In fact, I would say that this
function-returning-an-owning-raw-pointer is badly designed regardless of
the existence of Widget::Destroy(), because the raw pointer does not say
anything about its ownership and violates RAII. I would rather have
Widget Widget::Make(string widget_spec);
(value-semantics) or at least
std::shared_ptr<Widget> Widget::Make(string widget_spec);
(handle-semantics, but with the appropriate "destroyer" bundled alongside
the pointer).
> (If so, why do we have virtual destructors at all?)
Classical polymorphism. You could actually make the virtual destructor Do
The Right Thing in this case, but that would require that your object
instance know its own dynamic type. This type-safety is the cornerstone of
classical OOP. In C++ we would write something like this
<https://wandbox.org/permlink/01Kktq2zGjQswPvu>:
class fixed_string {
public:
static fixed_string *Make(const std::string &data) {
assert(data.size() < 256);
return make_impl(std::make_index_sequence<256>{}, data);
}
template<size_t... Is>
static fixed_string *make_impl(std::index_sequence<Is...>, const
std::string &data);
virtual ~fixed_string() =3D default;
protected:
size_t size_;
};
template<size_t N>
class inlined_fixed_string : public fixed_string {
char data_[N ? N : 1];
public:
inlined_fixed_string(const char *s) {
size_ =3D N;
memcpy(data_, s, N);
}
};
template<size_t... Is>
fixed_string *fixed_string::make_impl(std::index_sequence<Is...>, const
std::string &data) {
using FP =3D fixed_string* (*)(const char *);
FP arr[] =3D {
+[](const char *s) -> fixed_string* { return new
inlined_fixed_string<Is>(s); } ...
};
return arr[data.size()](data.data());
}
Now we have a base class with a virtual destructor, and a family of derived
classes all of which override that virtual destructor to do the correct
cleanup for each respective derived class. We cannot create new derived
classes at runtime =E2=80=94 we must specify at compile-time the complete s=
et of
derived classes that we care about =E2=80=94 but this is just the usual cos=
t of
type-safety. It's the same reason there's no std::visit(std::any).
Again, I would probably argue that returning raw owning pointers in the
classical-OOP fashion is a bad idea, and I'd encourage wrapping things up
in value-semantic wrappers and not using inheritance at all (at least not
in a way that's visible to the user-programmer). But if you must use
classical-OOP and raw owning pointers, then classical-OOP does provide some
tools for each derived instance to know how to destroy itself.
=E2=80=93Arthur
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/CADvuK0%2B5pBV7ee1OfT8c3DtP8XS6_7Dm1YnPH0G1TAE%2=
BqEDcTw%40mail.gmail.com.
--f403045c14682afdf3055bc3aaf4
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Tue, Oct 17, 2017 at 12:39 PM, Andrew Hunter <span dir=
=3D"ltr"><<a href=3D"mailto:ahh@google.com" target=3D"_blank">ahh@google=
..com</a>></span> wrote:<br><div class=3D"gmail_extra"><div class=3D"gmai=
l_quote"><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8e=
x;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-styl=
e:solid;padding-left:1ex"><span class=3D"gmail-">On Tue, Oct 17, 2017 at 11=
:30 AM, Arthur O'Dwyer<br>
<<a href=3D"mailto:arthur.j.odwyer@gmail.com">arthur.j.odwyer@gmail.com<=
/a>> wrote:<br>
> Yes, this is accurate. The programmer should never expect "delete=
<br>
> expressions" to work with objects that were not created via "=
;new<br>
> expressions".<br>
<br>
</span>Which programmer?<br></blockquote><div><br></div><div>All of them. ;=
)</div><div><br></div><div><br></div><blockquote class=3D"gmail_quote" styl=
e=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(2=
04,204,204);border-left-style:solid;padding-left:1ex">Are you=C2=A0saying t=
hat<br>
<br>
=C2=A0 =C2=A0 Widget *Widget::Make(string widget_spec);<br>
<br>
is badly designed unless I always also specify Widget::Destroy?</blockquote=
><div><br></div><div>Yes. In fact, I would say that this function-returning=
-an-owning-raw-pointer is badly designed regardless of the existence of Wid=
get::Destroy(), because the raw pointer does not say anything about its own=
ership and violates RAII. I would rather have</div><div><br></div><div>=C2=
=A0 =C2=A0 Widget Widget::Make(string widget_spec);</div><div><br></div><di=
v>(value-semantics) or at least</div><div><br></div><div>=C2=A0 =C2=A0 std:=
:shared_ptr<Widget> Widget::Make(string widget_spec);</div><div><br><=
/div><div>(handle-semantics, but with the appropriate "destroyer"=
bundled alongside the pointer).</div><div>=C2=A0</div><blockquote class=3D=
"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;borde=
r-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">(If=
=C2=A0so, why do we have virtual destructors at all?)</blockquote><div><br>=
</div><div>Classical polymorphism. You could actually make the virtual dest=
ructor Do The Right Thing in this case, but that would require that your ob=
ject instance know its own dynamic type. This type-safety is the cornerston=
e of classical OOP. In C++ we would write <a href=3D"https://wandbox.org/pe=
rmlink/01Kktq2zGjQswPvu">something like this</a>:</div><div><br></div><div>=
<div><font face=3D"monospace, monospace">class fixed_string {</font></div><=
div><font face=3D"monospace, monospace">public:</font></div><div><span styl=
e=3D"font-family:monospace,monospace">=C2=A0 =C2=A0 static fixed_string *Ma=
ke(const std::string &data) {</span><br></div><div><font face=3D"monosp=
ace, monospace">=C2=A0 =C2=A0 =C2=A0 =C2=A0 assert(data.size() < 256);</=
font></div><div><font face=3D"monospace, monospace">=C2=A0 =C2=A0 =C2=A0 =
=C2=A0 return make_impl(std::make_index_sequence<256>{}, data);</font=
></div><div><font face=3D"monospace, monospace">=C2=A0 =C2=A0 }</font></div=
><div><font face=3D"monospace, monospace">=C2=A0 =C2=A0=C2=A0</font></div><=
div><font face=3D"monospace, monospace">=C2=A0 =C2=A0 template<size_t...=
Is></font></div><div><font face=3D"monospace, monospace">=C2=A0 =C2=A0 =
static fixed_string *make_impl(std::index_sequence<Is...>, const std:=
:string &data);</font></div><div><font face=3D"monospace, monospace">=
=C2=A0 =C2=A0=C2=A0</font></div><div><font face=3D"monospace, monospace">=
=C2=A0 =C2=A0 virtual ~fixed_string() =3D default;</font></div><div><font f=
ace=3D"monospace, monospace">protected:</font></div><div><font face=3D"mono=
space, monospace">=C2=A0 =C2=A0 size_t size_;</font></div><div><font face=
=3D"monospace, monospace">};</font></div><div><font face=3D"monospace, mono=
space"><br></font></div><div><font face=3D"monospace, monospace">template&l=
t;size_t N></font></div><div><font face=3D"monospace, monospace">class i=
nlined_fixed_string : public fixed_string {</font></div><div><font face=3D"=
monospace, monospace">=C2=A0 =C2=A0 char data_[N ? N : 1];</font></div><div=
><font face=3D"monospace, monospace">public:</font></div><div><font face=3D=
"monospace, monospace">=C2=A0 =C2=A0 inlined_fixed_string(const char *s) {<=
/font></div><div><font face=3D"monospace, monospace">=C2=A0 =C2=A0 =C2=A0 =
=C2=A0 size_ =3D N;</font></div><div><font face=3D"monospace, monospace">=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 memcpy(data_, s, N);</font></div><div><font fac=
e=3D"monospace, monospace">=C2=A0 =C2=A0 }</font></div><div><font face=3D"m=
onospace, monospace">};</font></div><div><font face=3D"monospace, monospace=
"><br></font></div><div><font face=3D"monospace, monospace">template<siz=
e_t... Is></font></div><div><font face=3D"monospace, monospace">fixed_st=
ring *fixed_string::make_impl(std::index_sequence<Is...>, const std::=
string &data) {</font></div><div><font face=3D"monospace, monospace">=
=C2=A0 =C2=A0 using FP =3D fixed_string* (*)(const char *);</font></div><di=
v><font face=3D"monospace, monospace">=C2=A0 =C2=A0 FP arr[] =3D {</font></=
div><div><font face=3D"monospace, monospace">=C2=A0 =C2=A0 =C2=A0 =C2=A0 +[=
](const char *s) -> fixed_string* { return new inlined_fixed_string<I=
s>(s); } ...</font></div><div><font face=3D"monospace, monospace">=C2=A0=
=C2=A0 };</font></div><div><font face=3D"monospace, monospace">=C2=A0 =C2=
=A0 return arr[data.size()](data.data());</font></div><div><font face=3D"mo=
nospace, monospace">}</font></div></div><div><br></div><div>Now we have a b=
ase class with a virtual destructor, and a family of derived classes all of=
which override that virtual destructor to do the correct cleanup for each =
respective derived class. We cannot create new derived classes at runtime =
=E2=80=94 we must specify at compile-time the complete set of derived class=
es that we care about =E2=80=94 but this is just the usual cost of type-saf=
ety. It's the same reason there's no std::visit(std::any).</div><di=
v><br></div><div>Again, I would probably argue that returning raw owning po=
inters in the classical-OOP fashion is a bad idea, and I'd encourage wr=
apping things up in value-semantic wrappers and not using inheritance at al=
l (at least not in a way that's visible to the user-programmer). But if=
you must use classical-OOP and raw owning pointers, then classical-OOP doe=
s provide some tools for each derived instance to know how to destroy itsel=
f.</div><div><br></div><div>=E2=80=93Arthur</div></div></div></div>
<p></p>
-- <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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/CADvuK0%2B5pBV7ee1OfT8c3DtP8XS6_7Dm1Y=
nPH0G1TAE%2BqEDcTw%40mail.gmail.com?utm_medium=3Demail&utm_source=3Dfooter"=
>https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CADvuK0%2B5pB=
V7ee1OfT8c3DtP8XS6_7Dm1YnPH0G1TAE%2BqEDcTw%40mail.gmail.com</a>.<br />
--f403045c14682afdf3055bc3aaf4--
.