Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, skeptik_, Вы писали:
К>>>Запросто. Только на экзотических версиях STL.
_>>Ещё один искуственный пример skipped. Нормального примера не будет, насколько я понимаю.
К>Это не искусственный пример. Я с таким стрингом работал.
Это конечно не исключено, что какой-то ССЗБ такое написал, но обычно это делается через юнион.
Здравствуйте, Alexey F, Вы писали:
AF>Судя по имени — std::sort, судя по использованию — std::qsort (скорее, второе). Приведу примеры к обоим:
Поскольку qsort — функция стандартной библиотеки C и не имеет информации о типе элементов сортируемого массива (а только о размере), следует предположить, что она обменивает элементы через memcpy или memmove. Следовательно, её нельзя применять к не-POD-типам. Структура Entry содержит член типа std::string, не являющийся POD-типом.
Здравствуйте, Alexey F, Вы писали:
AF>Судя по имени — std::sort, судя по использованию — std::qsort (скорее, второе). Приведу примеры к обоим:
Не надо приводить примеры для qsort! Она не имеет права сортировать не-POD-ы, потому что вместо честного swap делает побайтовый обмен. А для std::string это может быть разрушительно.
К>Не надо приводить примеры для qsort! Она не имеет права сортировать не-POD-ы, потому что вместо честного swap делает побайтовый обмен. А для std::string это может быть разрушительно.
Здравствуйте, skeptik_, Вы писали:
К>>Запросто. Только на экзотических версиях STL.
_>Ещё один искуственный пример skipped. Нормального примера не будет, насколько я понимаю.
Это не искусственный пример. Я с таким стрингом работал.
Внутренний буфер резко увеличивает производительность, снимая нагрузку с менеджера памяти.
C>Поскольку qsort — функция стандартной библиотеки C и не имеет информации о типе элементов сортируемого массива (а только о размере), следует предположить, что она обменивает элементы через memcpy или memmove. Следовательно, её нельзя применять к не-POD-типам. Структура Entry содержит член типа std::string, не являющийся POD-типом.
Кстати, распространенное заблуждение. POD здесь не при чем. Здесь важно лишь можно ли объект перемещать в памяти или нет. Существуют не POD-типы, которые можно перемещать, и POD-типы, которые нельзя перемещать (например, если в структуре имеется указатель, указывающий на одно из полей этой же структуры). Можно ли перемещать std::string, зависит от его реализации, вполне может быть, что можно.
Здравствуйте, Кодт, Вы писали:
К>Не надо приводить примеры для qsort! Она не имеет права сортировать не-POD-ы, потому что вместо честного swap делает побайтовый обмен. А для std::string это может быть разрушительно.
Упс, точно, там std::string
Извиняюсь, параллельно код на C писал и мне там массив char померещился
Здравствуйте, Alexey F, Вы писали:
AF>const T& и T const& в данном случае (с указателями будут нюансы) эквивалентны, это вопрос привычки
Какие нюансы с указателями? const T* и T const* тоже идентичны.
Здравствуйте, Кодт, Вы писали:
AF>>const T& и T const& в данном случае (с указателями будут нюансы) эквивалентны, это вопрос привычки К>Какие нюансы с указателями? const T* и T const* тоже идентичны.
// вторая версия сообщения[сегодня у меня явно не лады с разъяснениями]
T const* и const T* — конечно эквивалентны. Я имел ввиду:
const T*
T* const// не эквивалентны
Лично меня, когда я узнал впервые про "const справа" это запутало (как считал я вначале — "а, значит, просто переносим const вправо и всё работает"); Решив предупредить об этом человека, спросившего про
Здравствуйте, Кодт, Вы писали:
К>>Не надо приводить примеры для qsort! Она не имеет права сортировать не-POD-ы, потому что вместо честного swap делает побайтовый обмен. А для std::string это может быть разрушительно.
К>http://codepad.org/KfaVSMzf — пример того, как qsort разрушает инвариант объекта. (this->myself==this)
искуственный пример. давайте пример как std::string разрушается.
Здравствуйте, skeptik_, Вы писали:
_>искуственный пример. давайте пример как std::string разрушается.
Запросто. Только на экзотических версиях STL.
Если string содержит буфер для маленькой строки сама в себе, и указатель на буфер — встроенный или внешний.
После побайтового обмена — содержимое буферов обменяется, это правильно, но заодно — строки будут ссылаться не на свои буферы, а перекрёстно. Дальнейшая судьба объектов со сломанным инвариантом туманна и грустна.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, skeptik_, Вы писали:
_>>искуственный пример. давайте пример как std::string разрушается.
К>Запросто. Только на экзотических версиях STL.
Ещё один искуственный пример skipped. Нормального примера не будет, насколько я понимаю.
Здравствуйте, skeptik_, Вы писали:
_>Ещё один искуственный пример skipped. Нормального примера не будет, насколько я понимаю.
Не такой уж и искусственный. С MSVC вроде как одно время поставлялся STL с такой строкой...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, skeptik_, Вы писали:
_>>Ещё один искуственный пример skipped. Нормального примера не будет, насколько я понимаю.
E>Не такой уж и искусственный. С MSVC вроде как одно время поставлялся STL с такой строкой...
Он до сих поставляется со small string optimization. Только делается она не столь нелепо, а через union указателя на внешний buffer и внутреннего buffer.
Здравствуйте, skeptik_, Вы писали:
К>>Запросто. Только на экзотических версиях STL.
_>Ещё один искуственный пример skipped. Нормального примера не будет, насколько я понимаю.
STLPort 5.2.1, оптимизация коротких строк включена — ИМХО совсем не экзотическая реализация. Падает при работе в отладчике при разрушении вектора на таком примере:
struct Entry{
std::string name;
int number;
};
int phoneSort ( void const* left, void const* right ) {
Entry const* leftEntry = static_cast<Entry const*> ( left ); // static_cast - более безопасный, нежели C-style cast ( struct Entry* )
Entry const* rightEntry = static_cast<Entry const*> ( right );
// -1, если меньше, 0, если равно и 1, если больше:if ( leftEntry->number < rightEntry->number ) {
return -1;
}
else if ( leftEntry->number == rightEntry->number ) {
return 0;
}
else {
return 1;
}
}
int main()
{
std::vector<Entry> book;
Entry e1 = {"222222222222222222", 2};
Entry e2 = {"1", 1};
book.push_back(e1);
book.push_back(e2);
std::qsort ( &book[ 0 ], book.size (), sizeof ( Entry ), &phoneSort );
return 0;
}
Здравствуйте, Тролль зеленый и толстый, Вы писали:
C>>Поскольку qsort — функция стандартной библиотеки C и не имеет информации о типе элементов сортируемого массива (а только о размере), следует предположить, что она обменивает элементы через memcpy или memmove. Следовательно, её нельзя применять к не-POD-типам. Структура Entry содержит член типа std::string, не являющийся POD-типом.
ТЗИ>Кстати, распространенное заблуждение. POD здесь не при чем. Здесь важно лишь можно ли объект перемещать в памяти или нет. Существуют не POD-типы, которые можно перемещать, и POD-типы, которые нельзя перемещать (например, если в структуре имеется указатель, указывающий на одно из полей этой же структуры). Можно ли перемещать std::string, зависит от его реализации, вполне может быть, что можно.
В действующем стандарте 2003 (выделены изменения между 2003 и 0x):
3.9 Types [basic.types]
2: For any object (other than a base-class subobject) of POD type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7) making up the object can be copied into an array of char or unsigned char.[36] If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value. [example/]
3: For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the value of obj1 is copied into obj2, using the memcpy library function, obj2 shall subsequently hold the same value as obj1. [example/]
В стандартопроекте C++0x, n3035:
3.9 Types [basic.types]
2: For any object (other than a base-class subobject) of trivially copyable type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7) making up the object can be copied into an array of char or unsigned char.[39] If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value. [example/]
3: For any trivially copyable type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the underlying bytes (1.7) making up obj1 are copied into obj2,[40] obj2 shall subsequently hold the same value as obj1. [example/]
40) By using, for example, the library functions (17.6.1.2) std::memcpy or std::memmove.
Далее:
3.9/9: Scalar types, trivially copyable class types (Clause 9), arrays of such types, and cv-qualified versions of these types (3.9.3) are collectively called trivially copyable types.
9/5: A trivially copyable class is a class that:
has no non-trivial copy constructors (12.8),
has no non-trivial copy assignment operators (13.5.3, 12.8), and
has a trivial destructor (12.4).
12.8/6: A copy constructor for class X is trivial if it is not user-provided (8.4) and if
class X has no virtual functions (10.3) and no virtual base classes (10.1), and
the constructor selected to copy each direct base class subobject is trivial, and
for each non-static data member of X that is of class type (or array thereof), the constructor selected to copy that member is trivial;
otherwise the copy constructor is non-trivial.
12.8/13 A copy assignment operator for class X is trivial if it is not user-provided and if
class X has no virtual functions (10.3) and no virtual base classes (10.1), and
the assignment operator selected to copy each direct base class subobject is trivial, and
for each non-static data member of X that is of class type (or array thereof), the assignment operator selected to copy that member is trivial;
otherwise the copy assignment operator is non-trivial.
12.4/3: […] A destructor is trivial if it is not user-provided and if:
the destructor is not virtual,
all of the direct base classes of its class have trivial destructors, and
for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.
An implicitly-declared destructor for a class X is defined as deleted if:
X is a union-like class that has a variant member with a non-trivial destructor,
any of the non-static data members has class type M (or array thereof) and M has an deleted destructor or a destructor that is inaccessible from the implicitly-declared destructor, or
any direct or virtual base class has a deleted destructor or a destructor that is inaccessible from the implicitly-declared destructor.
Otherwise, the destructor is non-trivial.
Далее, 21.4 перечисляет copy constructor, copy assignment operator и destructor. Все они, насколько я могу интерпретировать, относятся к user-provided и, следовательно, non-trivial. Таким образом, std::string (и любой класс, включающий std::string в качестве базы или члена) не является trivially copyable, следовательно, стандарт не гарантирует, что сортировка массива таких объектов через qsort будет корректна.
Кроме того, стандартопроект C++0x в 25.5/4 гласит:
both of which have the same behavior as the original declaration. The behavior is undefined unless the objects in the array pointed to by base are of trivial type.
— то есть налагается ограничение даже более сильное, чем trivially copyable type:
3.9/9: […] Scalar types, trivial class types (Clause 9), arrays of such types and cv-qualified versions of these types (3.9.3) are collectively called trivial types.
9/5: A trivial class is a class that has a trivial default constructor (12.1) and is trivially copyable.
Здравствуйте, Centaur, Вы писали:
ТЗИ>>Кстати, распространенное заблуждение. POD здесь не при чем. Здесь важно лишь можно ли объект перемещать в памяти или нет. Существуют не POD-типы, которые можно перемещать, и POD-типы, которые нельзя перемещать (например, если в структуре имеется указатель, указывающий на одно из полей этой же структуры). Можно ли перемещать std::string, зависит от его реализации, вполне может быть, что можно.
C>В действующем стандарте 2003 (выделены изменения между 2003 и 0x):
<...>
В общем-то, это буквоедство и дефектоскопия. В 2003 используется расширенное требование, POD, а в 0x — более узкое, trivally copyable.
А суть та же самая: если объект нетривиально копируется/присваивается/обменивается, то не надо его тривиально (побайтно) копировать/присваивать/обменивать
Даже если конкретный стандарт (2003) об этом умалчивает, всё равно "Нургалиев разрешил" является антипаттерном