Topic: empty_base and optional_base
Author: rhalbersma@gmail.com
Date: Tue, 4 Aug 2015 00:57:33 -0700 (PDT)
Raw View
------=_Part_2352_1157596572.1438675053713
Content-Type: multipart/alternative;
boundary="----=_Part_2353_233741638.1438675053713"
------=_Part_2353_233741638.1438675053713
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
In generic code, I sometimes want to conditionally add a data member to a c=
lass template. Writing separate template specializations scales as 2^N for =
N conditional data members. Absent a static_if feature, I have found class =
templates empty_base<T> and optional_base<T> useful:
namespace xstd {
namespace block_adl {template<class>struct empty_base{ empty_base() =
=3D default; template<class... Args> constexpr empty_base(Arg=
s&&...) {}};template<bool Condition, class T>using optional_base =3D std::c=
onditional_t<Condition, T, empty_base<T>>;} // namespace block_adl// =
deriving from empty_base or optional_base will not make xstd an associated =
namespace// this prevents ADL from finding overloads for e.g. begin/end/swa=
p from namespace xstdusing block_adl::empty_base;using block_adl::optional_=
base;} // namespace xstd
Exploiting the empty base optimization then allows users to write class tem=
plates that have optional data members (conditional on template variable tr=
aits Traits1_v and Traits2_v)
template<class T>
class Test
:
public Base0, // unconditional base class
public xstd::optional_base<Trait1_v<T>, Base1>,
public xstd::optional_base<Trait2_v<T>, Base2>
{
using B1 =3D xstd::optional_base<Trait1_v<T>, Base1>;
using B2 =3D xstd::optional_base<Trait2_v<T>, Base2>;
public:
Test() =3D default;
=20
Test(Arg0 const& a0, Arg1 const& a1, Arg2 const& a2)
:
Base0(a0),
B1(a1),
B2(a2)
{} =20
};
Note that the optional_base variadic constructor allows linear scaling of=
=20
constructor delegation in the user-defined class template Test. One minor=
=20
wart is that this constructor does require passing all parameters (but=20
otherwise, 2^N different constructors have to be written). But if any of=20
the traits variables evaluates to false, the corresponding parameter and=20
call to the base constructor will be optimized away. And because=20
optional_base also has a trivial default constructor, POD-ness of=20
user-defined classes is preserved by inheriting from it.=20
Obviously, over-use of inheritance has to be avoided, but the above design=
=20
does avoid quite a lot of code duplication. Oh, and I haven't tested this=
=20
with virtual functions, virtual base classes and what-not. I just use this=
=20
for assembling value-semantic classes out of conditional building blocks.
In any case, now that <type_traits> will get new metaprogramming features=
=20
as nonesuch and void_t, I wonder whether there is any demand for something=
=20
along the lines of empty_base and optional_base.
=20
--=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_2353_233741638.1438675053713
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><pre style=3D"font-size: 12px; line-height: 1.4;"><font fa=
ce=3D"arial, sans-serif">In generic code, I sometimes want to conditionally=
add a data member to a class template. Writing separate template specializ=
ations scales as 2^N for N conditional data members. Absent a </font><font =
color=3D"#0000ff" face=3D"courier new, monospace">static_if</font><font fac=
e=3D"arial, sans-serif"> feature, I have found class templates</font> <font=
face=3D"courier new, monospace" color=3D"#0000ff">empty_base<T></fon=
t> <font face=3D"arial, sans-serif">and</font> <font color=3D"#0000ff" face=
=3D"courier new, monospace">optional_base<T> </font><font face=3D"ari=
al, sans-serif"><font color=3D"#000000">useful</font>:</font></pre><pre sty=
le=3D"font-size: 12px; line-height: 1.4;"><span class=3D"k" style=3D"font-w=
eight: bold;"><font face=3D"courier new, monospace" color=3D"#0000ff"><br><=
/font></span></pre><pre style=3D"font-size: 12px; line-height: 1.4;"><span =
class=3D"k" style=3D"font-weight: bold;"><font face=3D"courier new, monospa=
ce" color=3D"#0000ff">namespace xstd {</font></span></pre><pre style=3D"fon=
t-size: 12px; line-height: 1.4;"><font face=3D"courier new, monospace" colo=
r=3D"#0000ff"><span class=3D"k" style=3D"font-weight: bold;">namespace</spa=
n> <span class=3D"n">block_adl</span> <span class=3D"p">{</span>
<a name=3D"type_traits.hpp-101"></a>
<a name=3D"type_traits.hpp-102"></a><span class=3D"k" style=3D"font-weight:=
bold;">template</span><span class=3D"o" style=3D"font-weight: bold;"><<=
/span><span class=3D"n">class</span><span class=3D"o" style=3D"font-weight:=
bold;">></span>
<a name=3D"type_traits.hpp-103"></a><span class=3D"k" style=3D"font-weight:=
bold;">struct</span> <span class=3D"n">empty_base</span>
<a name=3D"type_traits.hpp-104"></a><span class=3D"p">{</span>
<a name=3D"type_traits.hpp-105"></a> <span class=3D"n">empty_base</s=
pan><span class=3D"p">()</span> <span class=3D"o" style=3D"font-weight: bol=
d;">=3D</span> <span class=3D"k" style=3D"font-weight: bold;">default</span=
><span class=3D"p">;</span>
<a name=3D"type_traits.hpp-106"></a>
<a name=3D"type_traits.hpp-107"></a> <span class=3D"k" style=3D"font=
-weight: bold;">template</span><span class=3D"o" style=3D"font-weight: bold=
;"><</span><span class=3D"n">class</span><span class=3D"p">...</span> <s=
pan class=3D"n">Args</span><span class=3D"o" style=3D"font-weight: bold;">&=
gt;</span>
<a name=3D"type_traits.hpp-108"></a> <span class=3D"k" style=3D"font=
-weight: bold;">constexpr</span> <span class=3D"n">empty_base</span><span c=
lass=3D"p">(</span><span class=3D"n">Args</span><span class=3D"o" style=3D"=
font-weight: bold;">&&</span><span class=3D"p">...)</span> <span cl=
ass=3D"p">{}</span>
<a name=3D"type_traits.hpp-109"></a><span class=3D"p">};</span>
<a name=3D"type_traits.hpp-110"></a>
<a name=3D"type_traits.hpp-111"></a><span class=3D"k" style=3D"font-weight:=
bold;">template</span><span class=3D"o" style=3D"font-weight: bold;"><<=
/span><span class=3D"kt" style=3D"font-weight: bold;">bool</span> <span cla=
ss=3D"n">Condition</span><span class=3D"p">,</span> <span class=3D"k" style=
=3D"font-weight: bold;">class</span> <span class=3D"nc" style=3D"font-weigh=
t: bold;">T</span><span class=3D"o" style=3D"font-weight: bold;">></span=
>
<a name=3D"type_traits.hpp-112"></a><span class=3D"k" style=3D"font-weight:=
bold;">using</span> <span class=3D"n">optional_base</span> <span class=3D"=
o" style=3D"font-weight: bold;">=3D</span> <span class=3D"n">std</span><spa=
n class=3D"o" style=3D"font-weight: bold;">::</span><span class=3D"kt" styl=
e=3D"font-weight: bold;">conditional_t</span><span class=3D"o" style=3D"fon=
t-weight: bold;"><</span><span class=3D"n">Condition</span><span class=
=3D"p">,</span> <span class=3D"n">T</span><span class=3D"p">,</span> <span =
class=3D"n">empty_base</span><span class=3D"o" style=3D"font-weight: bold;"=
><</span><span class=3D"n">T</span><span class=3D"o" style=3D"font-weigh=
t: bold;">>></span><span class=3D"p">;</span>
<a name=3D"type_traits.hpp-113"></a>
<a name=3D"type_traits.hpp-114"></a><span class=3D"p">}</span> <span =
class=3D"c1" style=3D"font-style: italic;">// namespace block_adl</span>
<a name=3D"type_traits.hpp-115"></a>
<a name=3D"type_traits.hpp-116"></a><span class=3D"c1" style=3D"font-style:=
italic;">// deriving from empty_base or optional_base will not make xstd a=
n associated namespace</span>
<a name=3D"type_traits.hpp-117"></a><span class=3D"c1" style=3D"font-style:=
italic;">// this prevents ADL from finding overloads for e.g. begin/end/sw=
ap from namespace xstd</span>
<a name=3D"type_traits.hpp-118"></a><span class=3D"k" style=3D"font-weight:=
bold;">using</span> <span class=3D"n">block_adl</span><span class=3D"o" st=
yle=3D"font-weight: bold;">::</span><span class=3D"n">empty_base</span><spa=
n class=3D"p">;</span>
<a name=3D"type_traits.hpp-119"></a><span class=3D"k" style=3D"font-weight:=
bold;">using</span> <span class=3D"n">block_adl</span><span class=3D"o" st=
yle=3D"font-weight: bold;">::</span><span class=3D"n">optional_base</span><=
span class=3D"p">;</span>
<a name=3D"type_traits.hpp-120"></a>
<a name=3D"type_traits.hpp-121"></a><span class=3D"p">}</span> <span =
class=3D"c1" style=3D"font-style: italic;">// namespace xstd</span></font><=
/pre><pre style=3D"font-size: 12px; line-height: 1.4;"><font face=3D"courie=
r new, monospace" color=3D"#0000ff"><span class=3D"c1" style=3D"font-style:=
italic;"><br></span></font></pre><pre style=3D"font-size: 12px; line-heigh=
t: 1.4;"><font color=3D"#000000" face=3D"arial, sans-serif">Exploiting the =
empty base optimization then allows users to write class templates that hav=
e optional data members (conditional on template variable traits Traits1_v =
and Traits2_v)</font></pre><pre style=3D"font-size: 12px; line-height: 1.4;=
"><font face=3D"courier new, monospace" color=3D"#000000"><br></font></pre>=
<pre><font face=3D"courier new, monospace" color=3D"#0000ff"><span style=3D=
"font-size: 12px; line-height: 16.7999992370605px;">template<class T>
class Test
:
public Base0, // unconditional base class
public xstd::optional_base<Trait1_v<T>, Base1>,
public xstd::optional_base<Trait2_v<T>, Base2>
{
using B1 =3D xstd::optional_base<Trait1_v<T>, Base1>;
using B2 =3D xstd::optional_base<Trait2_v<T>, Base2>;
public:</span></font></pre><pre><font face=3D"courier new, monospace" color=
=3D"#0000ff"><span style=3D"font-size: 12px; line-height: 16.7999992370605p=
x;"> Test() =3D default;</span></font></pre><pre><font face=3D"courier n=
ew, monospace" color=3D"#0000ff"><span style=3D"font-size: 12px; line-heigh=
t: 16.7999992370605px;"> =20
Test(Arg0 const& a0, Arg1 const& a1, Arg2 const& a2)
:
Base0(a0),
B1(a1),
B2(a2)
{} =20
};</span><span style=3D"font-size: 12px; line-height: 1.4;">
</span></font></pre><div><font face=3D"courier new, monospace" color=3D"#00=
0000"><br></font></div><div><font color=3D"#000000" face=3D"arial, sans-ser=
if">Note that the </font><font face=3D"courier new, monospace" color=3D"#00=
00ff">optional_base</font><font color=3D"#000000" face=3D"arial, sans-serif=
"> variadic constructor allows linear scaling of constructor delegation in =
the user-defined class template </font><font color=3D"#0000ff" face=3D"cour=
ier new, monospace">Test</font><font color=3D"#000000" face=3D"arial, sans-=
serif">. One minor wart is that this constructor does require passing all p=
arameters (but otherwise, 2^N different constructors have to be written). B=
ut i</font><span style=3D"color: rgb(0, 0, 0); font-family: arial, sans-ser=
if;">f any of the traits variables evaluates to false, the corresponding pa=
rameter and call to the base constructor will be optimized away.</span><spa=
n style=3D"color: rgb(0, 0, 0); font-family: arial, sans-serif;">=C2=A0</sp=
an><font color=3D"#000000" face=3D"arial, sans-serif">And because </font><f=
ont face=3D"courier new, monospace" color=3D"#0000ff">optional_base</font><=
font color=3D"#000000" face=3D"arial, sans-serif"> also has a trivial defau=
lt constructor, POD-ness of user-defined classes is preserved by inheriting=
from it.=C2=A0</font></div><div><font color=3D"#000000" face=3D"arial, san=
s-serif"><br></font></div><div><font color=3D"#000000" face=3D"arial, sans-=
serif">Obviously, over-use of inheritance has to be avoided, but the above =
design does avoid quite a lot of code duplication. Oh, and I haven't te=
sted this with virtual functions, virtual base classes and what-not. I just=
use this for assembling value-semantic classes out of conditional building=
blocks.</font></div><div><font color=3D"#000000" face=3D"arial, sans-serif=
"><br></font></div><div><font color=3D"#000000" face=3D"arial, sans-serif">=
In any case, now that </font><font face=3D"courier new, monospace" color=3D=
"#0000ff"><type_traits></font><font color=3D"#000000" face=3D"arial, =
sans-serif"> will get new metaprogramming features as </font><font face=3D"=
arial, sans-serif" color=3D"#0000ff">nonesuch</font><font color=3D"#000000"=
face=3D"arial, sans-serif"> and </font><font face=3D"arial, sans-serif" co=
lor=3D"#0000ff">void_t</font><font color=3D"#000000" face=3D"arial, sans-se=
rif">, I wonder whether there is any demand for something along the lines o=
f=C2=A0</font><font face=3D"arial, sans-serif" color=3D"#0000ff">empty_base=
</font><font color=3D"#000000" face=3D"arial, sans-serif"> and </font><font=
face=3D"arial, sans-serif" color=3D"#0000ff">optional_base</font><font col=
or=3D"#000000" face=3D"arial, sans-serif">.</font></div><pre style=3D"font-=
size: 12px; line-height: 1.4;"><font face=3D"courier new, monospace" color=
=3D"#000000"><br></font></pre><pre style=3D"font-size: 12px; line-height: 1=
..4;"><span style=3D"color: rgb(0, 0, 0); font-family: 'courier new'=
, monospace; line-height: 1.4;"> </span><br></pre></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_2353_233741638.1438675053713--
------=_Part_2352_1157596572.1438675053713--
.