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 ]&][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 ]&][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 { ... }]&][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.]")