Слегка калечно (вообще именно в таких деталях система импорта Python с его import foo as bar или from moo import foo as bar — самая прямая и удобная), но работает же.
N>namespace sso { using namespace std::strong_ordering; }
У такого подхода есть преимучества против namespace alias (кроме совместимости со старыми компиляторами, в которых всё равно нет никаких ордерингов)?
Здравствуйте, netch80, Вы писали:
O>> if (r != 0) { O>> return (r < 0) ? std::strong_ordering::less : std::strong_ordering::greater; O>> } O>> return std::strong_ordering::equal;
N>namespace sso { using namespace std::strong_ordering; } N>return (r < 0) ? sso::less : sso::greater;
N>Слегка калечно (вообще именно в таких деталях система импорта Python с его import foo as bar или from moo import foo as bar — самая прямая и удобная), но работает же.
Во-первых, std::strong ordering — это класс, а не пространство имен. Поэтому просто: using sso = std::strong_ordering.
А если бы и было пространство имен, всё равно не стоило так извращаться. Алиасы для пространств имен тоже можно использовать: namespace fs = std::filesystem;
Ну и в-третьих, using-директивы (aka using namespace) нужно использовать очень осторожно. В идеале не использовать вообще. Ибо всё это бомбы отложенного действия.
--
Справедливость выше закона. А человечность выше справедливости.
C>Не понимаю, зачем эти std::strong_ordering::less хоть где-то явно использовать?
Так это был демонстрационный пример на использование слов из strong_ordering, а не на алгоритмическую часть.
Представьте себе, что вам надо написать сравнение строк, или номеров версий, которые несколько чисел через точку, или что-то ещё столь же иерархическое. Да, два символа или отдельных номера вы сравните через <=>, но тут же придётся делать условие типа return if rc != std::strong_ordering::equal, а если equal, то идти к следующему пункту.
Есть и более хитрые случаи.
Здравствуйте, rg45, Вы писали:
R>А если бы и было пространство имен, всё равно не стоило так извращаться. Алиасы для пространств имен тоже можно использовать: namespace fs = std::filesystem;
OK, спасибо. Уже успел забыть про них.
R>Ну и в-третьих, using-директивы (aka using namespace) нужно использовать очень осторожно. В идеале не использовать вообще. Ибо всё это бомбы отложенного действия.
В крошечном контролируемом контексте не должно быть страшно. Хотя тоже обидно, да — не могут не заложить диверсию даже в такой детали...
Здравствуйте, serg_joker, Вы писали:
N>>namespace sso { using namespace std::strong_ordering; } _>У такого подхода есть преимучества против namespace alias (кроме совместимости со старыми компиляторами, в которых всё равно нет никаких ордерингов)?
Есть преимущество: про него легче прочитать тому, кто прочно забыл детали, но помнит, что сходить на cppreference.com по слову using должно что-то подсказать
Здравствуйте, ononim, Вы писали:
O>Ватафак? Что за словоблудие? Я считаю комьюнити должно найти сильно разговорчивых членов комитета и расстрелять их. Ведь болтун — находка для шпиона. В данном случае — для раста.
Здравствуйте, netch80, Вы писали:
N>"duration не нужен" — это, как говорят автомобилисты, до первого столба, когда у тебя вдруг где-то образуется 4050 год потому, что неправильно применены объекты на сложение
Отладка таких ситуаций занимает меньше времени, чем выгугливание документации
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, netch80, Вы писали:
N>Стандартизация того, что корзины 1) есть, 2) их только один набор — очень жёстко зажимает реализацию до closed addressing hash map с рывочной пересборкой. По эффективности получается амортизированное O(1), но не стабильное O(1), как было раньше в Dinkumware и вслед Microsoft STL. А это значит, что критичные к постоянству времени приложения не могут его использовать.
А как в хеш-мапе может быть стабильное O(1)? Вот не повезло и хеши совпали — и что?
А STLьную мапу ограничивает в первую очередь требование стабильности указателей на элементы — всё, dense_map пролетает, примет аллокация на каждый элемент
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, netch80, Вы писали:
N>Представьте себе, что вам надо написать сравнение строк, или номеров версий, которые несколько чисел через точку, или что-то ещё столь же иерархическое. Да, два символа или отдельных номера вы сравните через <=>, но тут же придётся делать условие типа return if rc != std::strong_ordering::equal, а если equal, то идти к следующему пункту. N>Есть и более хитрые случаи.
Разработчики дизайна three-way comparison оказались чрезвычайно коварны и расшилири операции сравнения объектов класса std::strong_ordering, разрешив сравнение с ЛИТЕРАЛЬНЫМ нулём! Таким образом, выражение rc != std::strong_ordering::equal эквивалентно rc != 0. Допустимы выражения типа a <=> b < 0, a <=> b >= 0, etc. Похоже, им и самим не очень нравилось выписывать эти длинные имена
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, ononim, Вы писали:
o> N>"duration не нужен" — это, как говорят автомобилисты, до первого столба, когда у тебя вдруг где-то образуется 4050 год потому, что неправильно применены объекты на сложение, или 1 год вдруг преобразовался к юлианскому календарю и сократился на 10 дней потому, что вычитание. o> duration как самостоятельный объект — ок (хотя название конечно опять же — period было бы читабельнее и писабельнее), а вот duration в качестве скоупа для seconds — лишнее.
duration и period — разные сущности.
duration — это продолжительность чего-либо в тиках часов. Это как-бы физические тики часов между двумя моментами времени. И считается что в дне всегда 24 часа, в годе усреднённое кол-во дней и т.п.
period — это календарная разница по дням/месяцам/годам и может зависеть от таймзоны, календарных правил.
Здравствуйте, T4r4sB, Вы писали:
N>>Стандартизация того, что корзины 1) есть, 2) их только один набор — очень жёстко зажимает реализацию до closed addressing hash map с рывочной пересборкой. По эффективности получается амортизированное O(1), но не стабильное O(1), как было раньше в Dinkumware и вслед Microsoft STL. А это значит, что критичные к постоянству времени приложения не могут его использовать.
TB>А как в хеш-мапе может быть стабильное O(1)? Вот не повезло и хеши совпали — и что?
Разумеется, всё работает в предположении, что на один хэш приходится не более K элементов, где K — константа, ограниченная задачей. Иначе хэш-мапа вообще не может быть полезной, и надо переходить к чему-то другому — например, мапе на дереве (собственно std::map в C++). Извините, абсолютных чудес не бывает. (Кстати, в Java начиная с 8-й защита против таких перекосов: если у корзины более 8 элементов, она преобразуется в маленький самопальный TreeMap. Вместо O(N) худшего случая получаем в результате O(log N).)
Я же говорил о другом. Если в процессе операции, которая имеет право делать создание новых корзин и перераспределение по ним (для unordered_map это позволено для insert с родственными, но не для erase), движок подсчитает, что надо перераспределить по другому составу корзин, это и есть O(N) для данной операции, а если нет, O(1). То есть время одного insert (emplace, etc.) может оказаться непредсказуемо высоким. Как говорил герой фильма, "стабильности нет". Сейчас в библиотеках для GCC и Clang именно так, они грубым рывком пересчитывают и перемещают всех.
А вот названные старые реализации позволяли иметь одновременно два набора корзин. Например, идёт активное заполнение, первый на 1024 корзин, второй на 2048. Каждый insert вставляет новый элемент (ключ+значение) в новый набор (который на 2048) и заодно перемещает два элемента из старого в новый. Действие по перегруппировке между корзинами, таким образом, аккуратно размазывается на разные операции. Или при удалении элементов, наоборот, новый набор тоньше, действие по сути то же самое.
В принципе, можно подобное накостылять (именно что накостылять) и в рамках требований C++11. Возвращал bucket_count() число 1024. Решили создать новый набор, оп — уже 3072, старый плюс новый. Закончился перенос из старого — старый удалили — 2048. Только сжатие назад неудобно. На erase() оно запрещено. Значит, если после всех erase() будет один insert(), на него надо навесить дополнительно сколько-то перемещений (минимум 2, лучше 3) из опустыненного широкого набора в новый узкий. Вроде я никаких подводных камней не заметил, но кто знает.
TB>А STLьную мапу ограничивает в первую очередь требование стабильности указателей на элементы — всё, dense_map пролетает, примет аллокация на каждый элемент
Ну если в большинстве корзин 0 или 1 элемент (GCC поддерживает по умолчанию load factor, равный 1) — это совмещено с самими корзинами. Можно оставлять место и на ещё один, больше — вряд ли, надо рисовать списки (или деревья, как выше).
Здравствуйте, netch80, Вы писали:
N>Ну если в большинстве корзин 0 или 1 элемент (GCC поддерживает по умолчанию load factor, равный 1) — это совмещено с самими корзинами.
Каждая корзина — в своей области памяти. Все равно по аллокации на элемент
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, netch80, Вы писали:
N>>Ну если в большинстве корзин 0 или 1 элемент (GCC поддерживает по умолчанию load factor, равный 1) — это совмещено с самими корзинами.
TB>Каждая корзина — в своей области памяти. Все равно по аллокации на элемент
Почему ты так думаешь?
Смотрю на реализацию GCC. Есть массив корзин, динамически аллоцируемый целым куском. Каждый элемент корзины — структура из значения и указателя на следующий такой же элемент. Для простоты демонстрации это выглядит где-то так:
[c++]
struct bucket_element {
Value value; // по факту просто область памяти, если !present
bool present;
struct bucket_element *next;
};
struct hash_map {
struct bucket_element *bucket_array;
size_t bucket_array_size;
};
[/c++]
Массив выделяется куском.
Дополнительные аллокации bucket_element происходят только там, где в корзине более одного элемента. Первая в каждой корзине делается по месту.
Здравствуйте, netch80, Вы писали:
N>Здравствуйте, T4r4sB, Вы писали:
TB>>Здравствуйте, netch80, Вы писали:
N>>>Ну если в большинстве корзин 0 или 1 элемент (GCC поддерживает по умолчанию load factor, равный 1) — это совмещено с самими корзинами.
TB>>Каждая корзина — в своей области памяти. Все равно по аллокации на элемент
N>Почему ты так думаешь?
Пртому что указатели на элементы сохраняются при перестроении
N>Смотрю на реализацию GCC. Есть массив корзин, динамически аллоцируемый целым куском. Каждый элемент корзины — структура из значения и указателя на следующий такой же элемент. Для простоты демонстрации это выглядит где-то так:
N>[c++] N>struct bucket_element { N> Value value; // по факту просто область памяти, если !present N> bool present; N> struct bucket_element *next;
А еще там есть using __bucket_type=__node_base*
N>Массив выделяется куском.
Массив указателей на ноды
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте