mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 06:06:00 -06:00
157 lines
12 KiB
C++
157 lines
12 KiB
C++
TITLE("Moveable")
|
|
TOPIC_TEXT(
|
|
"[ $$0,0#00000000000000000000000000000000:Default][l288;i704;a17;O9;~~~.992; $$1,"
|
|
"0#10431211400427159095818037425705:param][a83;*R6 $$2,5#3131016247420302412518841"
|
|
"7583966:caption][b83;* $$3,5#07864147445237544204411237157677:title][b167;a42;C $"
|
|
"$4,6#40027414424643823182269349404212:item][b42;a42; $$5,5#4541300047534217475409"
|
|
"1244180557:text][l288;a17; $$6,6#27521748481378242620020725143825:desc][l321;t246"
|
|
";C@5;1 $$7,7#20902679421464641399138805415013:code][b2503; $$8,0#6514237545610002"
|
|
"3862071332075487:separator][*@(0.0.255) $$9,0#83433469410354161042741608181528:ba"
|
|
"se][t4167;C+117 $$10,0#37138531426314131251341829483380:class][l288;a17;*1 $$11,1"
|
|
"1#70004532496200323422659154056402:requirement][i416;b42;a42;O9;~~~.416; $$12,12#"
|
|
"10566046415157235020018451313112:tparam][b167;C $$13,13#9243045944346046191110808"
|
|
"0531343:item1][a42;C $$14,14#77422149456609303542238260500223:item2][*@2$(0.128.1"
|
|
"28) $$15,15#34511555403152284025741354420178:NewsDate][l321;*C$7 $$16,16#03451589"
|
|
"433145915344929335295360:result][l321;b83;a83;*C$7 $$17,17#0753155046352950537122"
|
|
"8428965313:result`-line][l160;t4167;*C+117 $$18,5#8860394944220582595880005322242"
|
|
"5:package`-title][{_}%EN-US [s2;%00-00 [%00-00 Moveable]&][s5; [%00-00 Moveable] "
|
|
"concept represents basic requirement for types stored in Vector flavor of contain"
|
|
"ers (namely Vector, [%00-00 BiVector], Index and [%00-00 VectorMap]). To explain "
|
|
"what is and why it is so important let us first create very primitive Vector-like"
|
|
" container template&][s7;%00-00 [%00-00 template <class T>]&][s7;%00-00 [%00-00 c"
|
|
"lass SimpleVector {]&][s7;%00-00 [%00-00 -|T *vector;]&][s7;%00-00 [%00-00 -|int"
|
|
" capacity;]&][s7;%00-00 [%00-00 -|int items;]&][s7;%00-00 &][s7;%00-00 [%00-00 -|"
|
|
"void ][%00-00* Expand()][%00-00 {]&][s7;%00-00 [%00-00 -|-|capacity = 2 * capaci"
|
|
"ty;]&][s7;%00-00 [%00-00 -|-|T *newvector = (T *) new char`[capacity * sizeof(T)`"
|
|
"];]&][s7;%00-00 [%00-00 -|-|for(int i = 0; i < items; i`++) {]&][s7;%00-00 [%00-0"
|
|
"0 -|-|-|][%00-00* new(newvector`[i`]) T(vector`[i`])][%00")
|
|
TOPIC_TEXT(
|
|
"-00 ;]&][s7;%00-00* [%00-00* -|-|-|][%00-00 vector`[i`].T`::~T();]&][s7;%00-00 ["
|
|
"%00-00 -|-|}]&][s7;%00-00 [%00-00 -|-|delete`[`] (char *) vector;]&][s7;%00-00 [%"
|
|
"00-00 -|-|vector = newvector;]&][s7;%00-00 [%00-00 -|}]&][s7;%00-00 [%00-00 publi"
|
|
"c:]&][s7;%00-00 [%00-00 -|void Add(const T`& x) {]&][s7;%00-00 [%00-00 -|-|if(ite"
|
|
"ms >= capacity) ][%00-00* Expand()][%00-00 ;]&][s7;%00-00 [%00-00 -|-|new(vector`"
|
|
"[items`++`]) T(x);]&][s7;%00-00 [%00-00 -|}]&][s7;%00-00 [%00-00 -|T`& operator`["
|
|
"`](int i) { return vector`[i`]; }]&][s7;%00-00 [%00-00 -|SimpleVector() {]&][s7;%"
|
|
"00-00 [%00-00 -|-|vector = NULL;]&][s7;%00-00 [%00-00 -|-|capacity = items = 0;]&"
|
|
"][s7;%00-00 [%00-00 -|}]&][s7;%00-00 [%00-00 -|~SimpleVector() {]&][s7;%00-00 [%0"
|
|
"0-00 -|-|for(int i = 0; i < items; i`++)]&][s7;%00-00 [%00-00 -|-|-|vector`[i`].T"
|
|
"`::~T();]&][s7;%00-00 [%00-00 -|-|delete`[`] (char *)vector;]&][s7;%00-00 [%00-00"
|
|
" -|}]&][s7; };&][s5; This [%00-00* SimpleVector] stores added elements in [* vect"
|
|
"or]. If there is not sufficient space left in [* vector], [%00-00* SimpleVector] "
|
|
"simply doubles its capacity using [* Expand] method. And this method is what inte"
|
|
"rests us - because [* Expand ]requires means to copy values of elements from the "
|
|
"original [* vector] to new allocated one. Above version uses placement new and co"
|
|
"py constructor for this purpose. This also means that [%00-00* SimpleVector] requ"
|
|
"ires T to have copy constructor (in standard C`++ library terms: it must be [/ co"
|
|
"py-constructible]). Now let us create a typical element that can be stored in suc"
|
|
"h container&][s7;%00-00 [%00-00 class SimpleString {]&][s7;%00-00 [%00-00 -|char "
|
|
"*text;]&][s7;%00-00 [%00-00 public:]&][s7;%00-00 [%00-00 -|SimpleString(const cha"
|
|
"r *txt) {]&][s7;%00-00 [%00-00 -|-|text = new char`[strlen(txt)`];]&][s7;%00-00 ["
|
|
"%00-00 -|-|strcpy(text, txt);]&][s7;%00-00 [%00-00 -|}]&][s7;%00-00 [%00-00 -|Sim"
|
|
"pleString(const SimpleString`& s) {]&][s7;%00-00 [%00-00 -|-|text = new char`[str"
|
|
"len(s.text)`];]&][s7;%00-00 [%00-00 -|-|strcpy(text, s.te")
|
|
TOPIC_TEXT(
|
|
"xt);]&][s7;%00-00 [%00-00 -|}]&][s7;%00-00 [%00-00 -|void operator=(const Simple"
|
|
"String`& s) {]&][s7;%00-00 [%00-00 -|-|delete`[`] text;]&][s7;%00-00 [%00-00 -|-|"
|
|
"text = new char`[strlen(s.text)`];]&][s7;%00-00 [%00-00 -|-|strcpy(text, s.text);"
|
|
"-|-|]&][s7;%00-00 [%00-00 -|}]&][s7;%00-00 [%00-00 -|SimpleString() {]&][s7;%00-0"
|
|
"0 [%00-00 -|-|delete`[`] text;]&][s7;%00-00 [%00-00 -|}]&][s7;%00-00 [%00-00 };]&"
|
|
"][s5; and see what happens when [%00-00* SimpleVector][%00-00 ]of [%00-00* Simpl"
|
|
"eString][%00-00 s] is expanded: First, copies of all elements are created, that m"
|
|
"eans allocating new storage for [* text] member of new element and copying source"
|
|
" [* text] to it using [%00-00* strcpy]. A moment later, [* Expand] invokes destru"
|
|
"ctor for element, thus deleting all [* text]s in the original elements. Does not "
|
|
"it seem we are wasting a lot of CPU cycles just to make copies of things that we "
|
|
"throw away moment later anyway ? What if instead of making copies we could find a"
|
|
" way to transfer original element's [* text] members to new one and somehow disal"
|
|
"low [* delete`[`] text] in destructor? See how primitive it can be:&][s7;%00-00 ["
|
|
"%00-00 template <class T>]&][s7;%00-00 [%00-00 class SimpleVector {]&][s7;%00-00 "
|
|
"[%00-00 -|T *vector;]&][s7;%00-00 [%00-00 -|int capacity;]&][s7;%00-00 [%00-00 -"
|
|
"|int items;]&][s7;%00-00 &][s7;%00-00 [%00-00 -|void Expand() {]&][s7;%00-00 [%00"
|
|
"-00 -|-|capacity = 2 * capacity;]&][s7;%00-00 [%00-00 -|-|T *newvector = (T *) ne"
|
|
"w char`[capacity * sizeof(T)`];]&][s7;%00-00* [%00-00* -|-|][%00-00 memcpy(newvec"
|
|
"tor, vector, items * sizeof(T));]&][s7;%00-00 [%00-00 -|-|delete`[`](char *)vecto"
|
|
"r;]&][s7;%00-00 [%00-00 -|-|vector = newvector;]&][s7;%00-00 [%00-00 -|}]&][s7;%0"
|
|
"0-00 [%00-00 public:]&][s7;%00-00 [%00-00 -|void Add(const T`& x) {]&][s7;%00-00 "
|
|
"[%00-00 -|-|if(items >= capacity) Expand();]&][s7;%00-00 [%00-00 -|-|new(vector`["
|
|
"items`++`]) T(x);]&][s7;%00-00 [%00-00 -|}]&][s7;%00-00 [%00-00 -|SimpleVector() "
|
|
"{]&][s7;%00-00 [%00-00 -|-|vector = NULL;]&][s7;%00-00 [%")
|
|
TOPIC_TEXT(
|
|
"00-00 -|-|capacity = items = 0;]&][s7;%00-00 [%00-00 -|}]&][s7;%00-00 [%00-00 -|"
|
|
"~SimpleVector() {]&][s7;%00-00 [%00-00 -|-|for(int i = 0; i < items; i`++)]&][s7;"
|
|
"%00-00 [%00-00 -|-|-|vector`[i`].T`::~T();]&][s7;%00-00 [%00-00 -|-|delete`[`] (c"
|
|
"har *)vector;]&][s7;%00-00 [%00-00 -|}]&][s7;%00-00 [%00-00 };]&][s5; For the mom"
|
|
"ent please ignore fact that by using [%00-00 memcpy] to move non-POD types we vio"
|
|
"late C`++ standard (we will discuss it later). Now, what we get here is exactly w"
|
|
"hat we wanted - instead of copy construction and destruction we simply copy raw b"
|
|
"inary data to the new location. This way we simply get [* text ]member of element"
|
|
"s to new expanded [* vector]. We need to invoke neither copy constructor nor dest"
|
|
"ructor when expanding vector. Not a single CPU cycle lost.&][s5;*/ [*/ Types that"
|
|
" can be moved in memory using ][%00-00*/ memcpy][*/ we call ]moveable.&][s5; Cle"
|
|
"arly not all types are moveable. Certainly, being moveable has a lot to do with s"
|
|
"toring references to object itself or to its parts. E.g.&][s7;%00-00 [%00-00 stru"
|
|
"ct Link {]&][s7;%00-00 [%00-00 -|Link *prev;]&][s7;%00-00 [%00-00 public:]&][s7;%"
|
|
"00-00 [%00-00 -|Link() { prev = NULL; }]&][s7;%00-00 [%00-00 -|Link(Link *"
|
|
"p) { prev = p; }]&][s7;%00-00 [%00-00 };]&][s5;%00-00 [%00-00 is ][%00-00* not][%"
|
|
"00-00 moveable, because memcpy-ing to new location would break existing links. W"
|
|
"e define these requirements for moveable types:]&][s5;i150;O0; If a type is funda"
|
|
"mental, it is moveable.&][s5;i150;O0; Moveable types must not have any virtual me"
|
|
"thod nor virtual bases.&][s5;i150;O0; Base classes (if any) and any instance vari"
|
|
"ables are moveable.&][s5;i150;O0; No method of moveable object can store directly"
|
|
" or indirectly any references to itself, its base subobjects or its instance vari"
|
|
"ables to any variable that exists outside the scope of the method (that is, when "
|
|
"the method terminates).&][s5; Example:&][s7;%00-00 [%00-00 struct Foo;]&][s7;%00-"
|
|
"00 &][s7;%00-00 [%00-00 Foo *global`_foo;]&][s0;%00-00 &]")
|
|
TOPIC_TEXT(
|
|
"[s7;%00-00 [%00-00 struct Foo {]&][s7;%00-00 [%00-00 -|int a;]&][s7;%00-00 [%00-"
|
|
"00 -|Foo *foo;]&][s7;%00-00 [%00-00 -|int *ptr;]&][s7;%00-00 [%00-00 public:]&][s"
|
|
"7;%00-00 [%00-00 -|void Set(Foo * f) {]&][s7;%00-00 [%00-00 -|-|foo = f;]&][s7;%0"
|
|
"0-00 [%00-00 -|}]&][s7;%00-00 [%00-00 -|void Ok1() {]&][s7;%00-00 [%00-00 -|-|Foo"
|
|
" *x = this;]&][s7;%00-00 [%00-00 -|// local variable will not exist outside metho"
|
|
"d]&][s7;%00-00 [%00-00 -|// -> does not prevent Foo from being moveable]&][s7;%00"
|
|
"-00 [%00-00 -|}]&][s7;%00-00 [%00-00 -|void Ok2() {]&][s7;%00-00 [%00-00 -|-|mems"
|
|
"et(`&a, 0, sizeof(int));]&][s7;%00-00 [%00-00 -|// pointer will not exist outside"
|
|
" method]&][s7;%00-00 [%00-00 -|// -> does not prevent Foo from being moveable]&]["
|
|
"s7;%00-00 [%00-00 -|}]&][s7;%00-00 [%00-00 -|void Bad1() {]&][s7;%00-00 [%00-00 -"
|
|
"|-|foo = this;]&][s7;%00-00 [%00-00 -|// member variable foo exists outside metho"
|
|
"d]&][s7;%00-00 [%00-00 -|// -> makes Foo non-moveable]&][s7;%00-00 [%00-00 -|}]&]"
|
|
"[s7;%00-00 [%00-00 -|void Bad2() {]&][s7;%00-00 [%00-00 -|-|ptr = `&a;]&][s7;%00-"
|
|
"00 [%00-00 -|// pointer to subobject stored, ptr exists outside method]&][s7;%00-"
|
|
"00 [%00-00 -|// -> makes Foo non-moveable]&][s7;%00-00 [%00-00 -|}]&][s7;%00-00 ["
|
|
"%00-00 -|void Bad3() {]&][s7;%00-00 [%00-00 -|-|global`_foo = this;]&][s7;%00-00 "
|
|
"[%00-00 -|// pointer stored to global variable]&][s7;%00-00 [%00-00 -|// -> makes"
|
|
" Foo non-moveable]&][s0;%00-00C@5;2 [%00-00;3 -|-|][%00-00 }]&][s0;%00-00C@5;2 [%"
|
|
"00-00 -|-|void Bad4(Foo`& another) {]&][s0;%00-00C@5;2 [%00-00 -|-|-|another.Set("
|
|
"this);]&][s0;%00-00C@5;2 [%00-00 -|-|// pointer stored indirectly in object that "
|
|
"exist outside method]&][s0;%00-00C@5;2 [%00-00 -|-|// -> makes Foo non-moveable]&"
|
|
"][s0;%00-00C@5;2 [%00-00 -|-|}]&][s7;%00-00 [%00-00 };]&][s5; This requirements s"
|
|
"atisfies fairly large number of types, incidentally most of types you ever wanted"
|
|
" to store in container of elements of single type are moveable. Most important, a"
|
|
"ll [%00-00 NTL] containers [* are ]moveable.&][s5; Now we")
|
|
TOPIC_TEXT(
|
|
" have effective method how to organize storing of elements in containers. What w"
|
|
"e have to deal with is fact that being moveable is part of object's interface. To"
|
|
" make things safer we should disallow storing non-moveable elements in containers"
|
|
" requiring moveable ones. We need to have a way how to declare that type is movea"
|
|
"ble at compile time and also how to check it.&][s5; To achieve this goal, you can"
|
|
" mark moveable type using [%00-00* Moveable] base e.g.:&][s7;%00-00 [%00-00 class"
|
|
" SimpleString : Moveable<SimpleString> { ... }]&][s5;%00-00 [%00-00 or add you ca"
|
|
"n use ][%00-00* NTL`_MOVEABLE ][%00-00 macro to mark types where you cannot chang"
|
|
"e class interface:]&][s7;%00-00 [%00-00 NTL`_MOVEABLE(std`::string);]&][s5;%00-00"
|
|
" [%00-00 Now that we can mark types to be moveable, we need a way to check a type"
|
|
" for being moveable. You can do it by adding]&][s7;%00-00 [%00-00 AssertMoveable<"
|
|
"T>()]&][s5;%00-00 [%00-00 to some method of template that gets compiled for any t"
|
|
"emplate argument - destructor being the most obvious place. This function is in t"
|
|
"urn defined only if ][%00-00* T][%00-00 is marked as Moveable.]&][s5;%00-00 [%00"
|
|
"-00 Finally, time has come to deal with C`++ standard. Current C`++ defines memcp"
|
|
"y only for POD types. Moveable concept requires memcpy of non-POD types to be def"
|
|
"ined. In fact, difference between POD and Moveable non-POD types is existence of "
|
|
"constructor and non-virtual destructor. To get things work, all we need is that r"
|
|
"esult of memcpy-ing non-POD type ][%00-00* T ][%00-00 is same as memcpy-ing POD ]"
|
|
"[%00-00* T1 ][%00-00 type you would get by removing destructor and constructor fr"
|
|
"om ][%00-00* T][%00-00 . While this is still undefined by C`++, it is realy hard "
|
|
"to imagine optimal C`++ implementation that would break this rule. Indeed, all cu"
|
|
"rrent implementation we have met so far support moveable semantics in a way we ha"
|
|
"ve defined here. Performance and other gains realized using moveable concept are "
|
|
"too big to ignore.]")
|