Если у меня есть совокупность целочисленных типов, и я хочу создать ее экземпляр со случайным образом, безопасно ли здесь использовать reinterpret_cast?
template <typename T>
auto random() -> T {
static auto random_device = std::random_device{};
static auto generator = std::mt19937(random_device());
static auto distribution = std::uniform_int_distribution<std::uint8_t>{};
auto bytes = std::array<std::uint8_t, sizeof(T)>{};
for (auto i = std::size_t{0}; i < bytes.size(); ++i) {
bytes[i] = distribution(generator);
}
return *reinterpret_cast<T *>(bytes.data());
}
Почему бы не написать специальную функцию для вашего агрегата, которая правильно устанавливает для всех членов или элементов случайное значение?
Это небезопасно, строгое нарушение правил псевдонимов. Переработайте его в обратном приведении от T* к byte* или char*, что должно быть с static_cast.
@user207421 user207421 Почему вы убрали почтение?
Для C++23 есть std::start_lifetime_as
Я, вероятно, должен упомянуть, что я использую GCC-10, поэтому bit_cast недоступен
@Someprogrammerdude Я могу, но это повторяется, поэтому я не хочу этого делать
@PepijnKramer GCC-10, так что для меня нет С++ 23
Большую часть повторения можно абстрагировать в классы или функции вместе с перегрузками. Но с другой стороны, у вас будет что-то безопасное, портативное и что нельзя будет использовать не по назначению (в вашем коде нет ничего, что мешало бы кому-то использовать random<std::string>(), что не даст очень хорошего результата).





Для универсального типа T это может быть небезопасно.
Согласно справочнику c++, reinterpret_cast разрешен, если:
- Любой указатель объекта типа T1* может быть преобразован в другой объект. тип указателя cv T2*. Это точно эквивалентно static_cast<cv T2*>(static_cast<cv void*>(expression)) (из чего следует, что если T2 требование выравнивания не строже, чем у T1, значение указатель не меняется и преобразование полученного указателя обратно исходному типу дает исходное значение). В любом случае, Результирующий указатель может быть безопасно разыменован только в том случае, если это разрешено правила алиасинга типов (см. ниже).
Итак, вы можете сделать бросок от *std::uint8_t до *T. Но проблема в том, когда вы можете разыменовать результирующий указатель на T?
Не для каждого типа T это разрешено.
На той же странице сказано, что вы можете разыменовать AliasedType T, если:
AliasedType — это std::byte, (начиная с C++17) char или unsigned char: это позволяет исследовать объектное представление любого объекта как массив байтов.
Таким образом, если вы ограничите использование своей подпрограммы std::byte, char или unsigned char (как указано выше во второй цитате), да, вы можете безопасно разыменовать указатель в операторе return, и ваша функция должна быть безопасной.
Но вам лучше рассмотреть возможность использования std::byte для хранения общего буфера необработанной памяти.
to the types T mentioned above единственный тип, упомянутый выше, это std::uint8_t, верно? Вы также упоминаете std::byte, char and unsigned char. Итак, допустимы только std::uint8_t, std::byte, char и unsigned char T? Мне сложно пояснить "упомянутое выше".
you can do the cast from *std::uint8_t to *T ну да но which implies that if T2's alignment requirement is not stricter than T1's. Когда требования к выравниванию T2 более строгие, значение результирующего указателя не указывается. Я не думаю, что это так уж безопасно. eel.is/c++draft/expr#static.cast-14@KamilCuk о выравнивании: конечно, это то, что я цитирую из справочника С++. Я думаю, что это не относится к простому преобразованию в std::byte, char или unsigned char. Это имело бы значение для другого псевдонима, подробно описанного в ссылке.
По какой-то причине какой-то пользователь модифицировал мой пост и изменил его семантику, так и должно быть return *reinterpret_cast<T *>(bytes.data());
@UyHà Я изменил ответ в соответствии с типом возврата вашей функции.
Firstly this will not compile in the general case. You cannot reinterpret_castinto an arbitrary type. IfT` не является типом указателя или целочисленным типом, который не будет компилироваться.
Чтобы исправить это, вы, вероятно, захотите вернуться
return *reinterpret_cast<T*>(bytes.data());
Но и этого не делайте. Если T не является ((un)signed) char или std::byte, это поведение undefined.
Правильный способ реализации такой функции — сначала убедиться, что целевой тип T удовлетворяет критериям для этого. Я ограничу это, потребовав, чтобы это было тривиально:
decltype(auto) get_mt19937() {
static auto random_device = std::random_device{};
static auto generator = std::mt19937(random_device());
return generator;
}
template <typename T>
auto random() -> T {
static_assert(std::is_trivial_v<T>, "T must be trivial");
// This is bad. It will instantiate a new mt19937 engine for each type `T`.
//static auto random_device = std::random_device{};
//static auto generator = std::mt19937(random_device());
auto& generator = get_mt19937();
// no need to static
auto distribution = std::uniform_int_distribution<unsigned char>{};
auto return_val = T{};
// reinterpret_cast'ing to std::uint8_t* might not be legal
auto* bytes = reinterpret_cast<unsigned char*>(&return_val);
for (auto i = std::size_t{0}; i < sizeof(T); ++i) {
bytes[i] = distribution(generator);
}
return return_val;
}
Мой пост был изменен кем-то, и его семантика изменилась, оператор возврата должен быть `return *reinterpret_cast<T *>(bytes.data());`
Рассмотрите возможность использования std::bit_cast. Если
Tлегко скопировать, то это безопасно.