Вопрос или проблема
Я ищу законные примеры использования std::bit_cast
в своих кодовых базах. Однако текущая документация cppreference заставляет меня задуматься, может ли это вызвать накладные расходы из-за создания (именованного) временного объекта (и, таким образом, не подлежащего обязательному исключению копирования/перемещения).
Если посмотреть на стандарт, то он также утверждает, что возвращает простой объект, который должен быть скопирован.
Действительно ли есть накладные расходы, каковы преимущества по сравнению с простым std::memcpy
для существующего объекта?
В качестве иллюстрации вот вымышленный пример, где, насколько я понимаю, std::bit_cast
подразумевает значительно больше ассемблера, чем std::memcpy
(это не лучший критерий, но я не смог найти более простой способ проиллюстрировать свой вопрос):
//header.h
#ifndef HEADER
#define HEADER
#include <array>
class S {
public:
int val[1024] = {-1};
};
void SetBC(S& s,const std::array<char,sizeof(S)>& arr);
void SetMC(S& s,const std::array<char,sizeof(S)>& arr);
#endif
// main.cpp
#include "header.h"
#include <bit>
#include <cstring>
void SetBC(S& s,const std::array<char,sizeof(S)>& arr)
{
s = std::bit_cast<S>(arr);
}
void SetMC(S& s,const std::array<char,sizeof(S)>& arr)
{
std::memcpy(&s,arr.data(),sizeof(S));
}
Ответ или решение
Использование std::bit_cast
в C++ действительно вызывает некоторые вопросы относительно производительности и потенциальных накладных расходов. Разберёмся в этом подробнее.
Обзор std::bit_cast
std::bit_cast
— это функция, представленная в C++20, предназначенная для безопасного и эффективного преобразования между типами, не изменяя при этом представление данных. Она позволяет интерпретировать память одного типа как память другого типа, что может быть полезно в ряде случаев, например, при низкоуровневой работе с памятью и при реализации сериализации.
Накладные расходы std::bit_cast
-
Временные объекты:
Согласно стандарту,std::bit_cast
выполняет копирование результата в возвращаемый объект. Это значит, что если возвращаемый тип не является тривиальным (например, класс с пользовательскими конструкторами), это может привести к дополнительным накладным расходам на его копирование, если пространство для хранения результата не может быть использовано напрямую без создания временного объекта. -
Проверка типов:
В отличие отstd::memcpy
, которое просто копирует байты между двумя участками памяти,std::bit_cast
выполняет строгую проверку на совместимость типов. Это требует дополнительных операций времени выполнения, например, проверки на соответствие размеров и возможных ограничений на типы, что может добавить накладные расходы. -
Эффект оптимизации компилятора:
Если компилятор не может применить оптимизацию копирования, то накладные расходы станут ощутимыми. Например, если копирование не может использоваться при оптимизации (например, копирование не может быть устранено),std::bit_cast
может привести к более сложному ассемблерному коду по сравнению с использованиемstd::memcpy
.
Сравнение с std::memcpy
Использование std::memcpy
может оказаться более эффективным для копирования простых структур данных или массивов, так как оно отрабатывает быстрее и не требует дополнительных проверок. Однако при этом стоит помнить о следующих моментах:
-
Безопасность:
std::bit_cast
предоставляет дополнительные гарантии безопасности, в то время какstd::memcpy
не проверяет типы и может привести к неопределённому поведению при неправильном использовании. -
Семантика:
std::bit_cast
предназначен для более семантически правильного и безопасного преобразования типов, поэтому в контексте современного программирования предпочтительно использовать его.
Практические примеры
На практике, если ваше приложение требует высокой производительности и работа с простыми типами, например, массивами или структурами без пользовательских методов, то использование std::memcpy
может оказаться более подходящим выбором. Однако, если безопасность типов и правильность преобразований более важны, особенно в коде, который будет поддерживаться долгое время, то std::bit_cast
может быть более разумным выбором.
Заключение
Таким образом, накладные расходы, связанные с использованием std::bit_cast
, могут различаться в зависимости от контекста. В общем случае, при загрузке обычных типов или простых структур лучше использовать std::memcpy
, если вы можете гарантировать, что типы совместимы и безопасны для копирования. В противном случае, для более сложных и безопасных преобразований предпочтителен std::bit_cast
, даже если это может привести к некоторым дополнительным накладным расходам.