Я тут набросал пару примеров, как можно быстро двигать вектора.
Второй пример на VC++ 7.1 генерирует, по-видимому, неулучшаемый код.
; 114 :
; 115 : FastMoveVector(mem2,vec1,vec1+Size);
lea ecx, DWORD PTR _mem1$[esp+200]
lea edx, DWORD PTR _mem2$[esp+200]
xor eax, eax
npad 8
$L14160:
mov esi, DWORD PTR _mem1$[esp+eax+196]
mov DWORD PTR _mem2$[esp+eax+196], esi
mov esi, DWORD PTR [ecx]
add eax, 8
mov DWORD PTR [edx], esi
lea esi, DWORD PTR _mem1$[esp+eax+196]
lea ebp, DWORD PTR _mem1$[esp+276]
add ecx, 8
add edx, 8
cmp esi, ebp
jb SHORT $L14160
-- это memcpy скорость.
Единственный момент здесь -- у меня есть неясность с одним абзацем в стандарте. 3.8 4
A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a nontrivial destructor. For an object of a class type with a nontrivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called
and any program that depends on the side effects produced by the destructor has undefined behavior.
Пример 1.
/* MoveVector.cpp */
#include <iostream>
using namespace std;
/* Replace<T>() */
template <class T>
inline T Replace(T &var,T val) { T ret=var; var=val; return ret; }
/* class MoveSrc<T> */
template <class T>
class MoveSrc
{
T *src;
public:
MoveSrc(T &x) : src(&x) {}
T * operator -> () const { return src; }
};
/* class String */
class String
{
char *str;
size_t len;
String(const String &) {}
void operator = (const String &) {}
static char * GetNull();
public:
String() : str(GetNull()),len(0) {}
String(const char *str_);
explicit String(MoveSrc<String> x)
{
str=Replace(x->str,GetNull());
len=Replace(x->len,size_t(0));
}
~String();
const char * getStr() const { return str; }
size_t getLen() const { return len; }
String * moveObjectTo(void *mem)
{
__assume(mem);
return new(mem) String(MoveSrc<String>(*this));
}
};
char * String::GetNull()
{
static char zero[1];
return zero;
}
String::String(const char *str_)
{
len=strlen(str_);
str=new char[len+1];
memcpy(str,str_,len+1);
cout << "new char[" << len+1 << "]\n" ;
}
String::~String()
{
if( str!=GetNull() )
{
delete[] str;
cout << "delete char[]\n" ;
}
}
/* MoveVector<T>() */
template <class T>
void MoveVector(void *mem,T *ptr,T *lim) throw()
{
for(; ptr<lim ;ptr++,mem=static_cast<char *>(mem)+sizeof (T))
{
ptr->moveObjectTo(mem);
(*ptr).~T();
}
}
/* main() */
const size_t Size=10;
int main()
{
char mem1[Size][sizeof (String)];
char mem2[Size][sizeof (String)];
String *vec1=static_cast<String *>((void *)mem1);
String *vec2=static_cast<String *>((void *)mem2);
for(size_t i=0; i<Size ;i++) new(mem1[i]) String("string");
for(size_t i=0; i<Size ;i++) cout << vec1[i].getStr() << '\n' ;
MoveVector(mem2,vec1,vec1+Size);
for(size_t i=0; i<Size ;i++) cout << vec2[i].getStr() << '\n' ;
for(size_t i=0; i<Size ;i++) vec2[i].~String();
return 0;
}
Пример 2.
/* FastMoveVector.cpp */
#include <iostream>
using namespace std;
/* class FastMoveSrc<T> */
template <class T>
class FastMoveSrc
{
T *src;
public:
FastMoveSrc(T &x) : src(&x) {}
T * operator -> () const { return src; }
};
/* class String */
class String
{
char *str;
size_t len;
String(const String &) {}
void operator = (const String &) {}
static char * GetNull();
public:
String() : str(GetNull()),len(0) {}
String(const char *str_);
explicit String(FastMoveSrc<String> x)
{
str=x->str;
len=x->len;
// Исходный объект после этой операции становится невалидным и не должен больше использлваться -- включая вызов деструктора
}
~String();
const char * getStr() const { return str; }
size_t getLen() const { return len; }
String * fastMoveObjectTo(void *mem)
{
__assume(mem);
return new(mem) String(FastMoveSrc<String>(*this));
}
};
char * String::GetNull()
{
static char zero[1];
return zero;
}
String::String(const char *str_)
{
len=strlen(str_);
str=new char[len+1];
memcpy(str,str_,len+1);
cout << "new char[" << len+1 << "]\n" ;
}
String::~String()
{
if( str!=GetNull() )
{
delete[] str;
cout << "delete char[]\n" ;
}
}
/* FastMoveVector<T>() */
template <class T>
void FastMoveVector(void *mem,T *ptr,T *lim) throw()
{
for(; ptr<lim ;ptr++,mem=static_cast<char *>(mem)+sizeof (T))
{
ptr->fastMoveObjectTo(mem);
// Считаем объект убитым и не вызываем деструктор.
}
}
/* main() */
const size_t Size=10;
int main()
{
char mem1[Size][sizeof (String)];
char mem2[Size][sizeof (String)];
String *vec1=static_cast<String *>((void *)mem1);
String *vec2=static_cast<String *>((void *)mem2);
for(size_t i=0; i<Size ;i++) new(mem1[i]) String("string");
for(size_t i=0; i<Size ;i++) cout << vec1[i].getStr() << '\n' ;
FastMoveVector(mem2,vec1,vec1+Size);
for(size_t i=0; i<Size ;i++) cout << vec2[i].getStr() << '\n' ;
for(size_t i=0; i<Size ;i++) vec2[i].~String();
return 0;
}
... << RSDN@Home 1.1.0 stable >>
Здравствуйте, Шахтер, Вы писали:
Наверное уже поздно, но я не могу понять, а где вы используете эффект от поведения деструктора?
... << RSDN@Home 1.1.3 stable >>
Здравствуйте, Shady, Вы писали:
S>Здравствуйте, Шахтер, Вы писали:
S>Наверное уже поздно,
Не совсем понял, что значит поздно.
S>но я не могу понять, а где вы используете эффект от поведения деструктора?
Нигде. Я просто не могу понять, что подразумевает стандарт под "побочными эффектами, производимыми деструктором", и что значит "зависеть от них".
... << RSDN@Home 1.1.0 stable >>
Здравствуйте, Шахтер, Вы писали:
Ш>Не совсем понял, что значит поздно.
По времени
был час ночи...
S>>но я не могу понять, а где вы используете эффект от поведения деструктора?
Ш>Нигде. Я просто не могу понять, что подразумевает стандарт под "побочными эффектами, производимыми деструктором", и что значит "зависеть от них".
да,
, самому интерестно...
... << RSDN@Home 1.1.3 stable >>
Шахтер:
> неясность с одним абзацем в стандарте. 3.8 4
> <...> For an object of a class type with a nontrivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.
Грубо говоря, если:
в деструкторе есть существенные для программы побочные эффекты, выходящие за рамки деструктора (то есть, ближе к формулировке стандарта: программа будет выполняться по-разному в зависимости от того, вызван деструктор или нет)
и программа этот деструктор для старого объекта не вызвала перед тем, как создать на его месте новый,
то в ней неопределенное поведение.
Posted via RSDN NNTP Server 1.9 gamma
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Шахтер:
>> неясность с одним абзацем в стандарте. 3.8 4
>> <...> For an object of a class type with a nontrivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.
ПК>Грубо говоря, если:
ПК> в деструкторе есть существенные для программы побочные эффекты, выходящие за рамки деструктора (то есть, ближе к формулировке стандарта: программа будет выполняться по-разному в зависимости от того, вызван деструктор или нет)
ПК> и программа этот деструктор для старого объекта не вызвала перед тем, как создать на его месте новый,
ПК>то в ней неопределенное поведение.
Пока что яснее не стало.
... << RSDN@Home 1.1.0 stable >>