Comments 9
Прозрачное сравнение это же не обязательно под строки? Концепт, подходящий только под частный случай — как-то не ок.
Почему вообще код (ниже процитирован код из статьи) закладывается на то, что K — это std::string?
template <typename K>
void insert(K&& key) {
using CleanKey = std::remove_cvref_t<K>; // Удалили const, &, volatile
static_assert(std::is_same_v<CleanKey, std::string>, "Должен быть std::string!");
// ...
}
Эээ.. Каким образом это вообще
insert
? Да, что вы разошлись — тут вы правы
void insert(Key key, Value value) {
std::atomic_ref<Key>(key).store(key);
}
Это если по поверхности пройтись.
Тут нет никакой оптимизации для trivially copyable. У вас тут буквально семантически эквивалентный код. Семантически эквивалентный, но вы во втором случае зачем-то делаете бесполезный piecewise construct.
template <typename K>
void insert(K&& key, Value&& value) {
if constexpr (std::is_trivially_copyable_v<Value>) {
// Быстрая вставка для простых типов (int, double и т.д.)
buckets.emplace_back(std::forward<K>(key), std::forward<Value>(value));
} else {
// Медленная, но безопасная для сложных типов
buckets.emplace_back(std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(key)),
std::forward_as_tuple(std::forward<Value>(value)));
}
}
4) да, я тут поглядел и фигню написал. Можно спокойно сжать до
template <typename K>
void insert(K&& key, Value&& value) {
buckets.emplace_back(std::forward<K>(key), std::forward<Value>(value));
}
И этого будет достаточно.
Тут видимой была бы эффективность piecewise_construct при конструировании std::pair или std::tuple из нескольких аргументов, что бы избежать лишних промежуточных копирований и перемещений.
Так то, что бы просто показать работу if constexpr можно разделить на передачу для маленьких тривиально копируемых типов (без piecewise_construct) и для нетривиальных типов (с piecewise_construct).
Для больших тривиально копируемых типов скорее всего не будет смысла делать, т.к. разница будет практически незначительной.
Ну даже так отрывок кода выше будет прекрасно справляться.
p.s. Благодарю за замечание.
1-2) вы в праве реализовать это так, как вам угодно, вы ничем не ограничены. Это лишь моя идея их реализации.
3) Это абстрактный пример, конечно, его можно подогнать под более реальную версию insert, я просто посчитал, что для демонстрации идеи этого будет достаточно.
У меня вопрос про [[no_unique_address]]
А нет ли в компиляторах реализации своих кастомных атрибутов для до c++20 эпохи, так чтобы я мог наделать макросов и использовать [[no_unique_address]] фичу в c++17?
Ну на самом деле в 17 такого нет, насколько мне вообще известно, но если вам важно определить - сжимаем ли объект, то вы можете это сделать через наследование (EBO)
https://en.cppreference.com/w/cpp/language/ebo
struct Empty {};
struct S : private Empty { // EBO
int x;
};
static_assert(sizeof(S) == sizeof(int));
Конечно, это вообще никак не замена, но к сожалению других способов я не знаю. Если найдете, то буду рад послушать.
Можно использовать boost::compressed_pair, где как раз реализовано сжатие с помощью EBO, чтобы не писать это самому.
https://www.boost.org/doc/libs/1_86_0/libs/utility/doc/html/utility/utilities/compressed_pair.html
Подумал было, что это уже должно работать в стандартном `std::tuple`, но как оказалось оно implementation defined. Плюс для msvc EBO оказывается нужно включать специально.
Пример https://godbolt.org/z/dhEGfqnbz
Хеш-таблица и C++20