Как использовать новый тип std:: byte в местах, где требуется статический стиль unsigned char?
std::byte
- это новый тип в С++ 17, который выполнен как enum class byte : unsigned char
. Это делает невозможным его использование без соответствующего преобразования. Итак, я сделал псевдоним для вектора такого типа для представления массива байтов:
using Bytes = std::vector<std::byte>;
Однако его нельзя использовать в старом стиле: функции, которые принимают его как параметр, терпят неудачу, потому что этот тип не может быть легко преобразован в старый тип std::vector<unsigned char>
, например, использование библиотеки zipper
/resourcecache/pakfile.cpp: In member function 'utils::Bytes resourcecache::PakFile::readFile(const string&)':
/resourcecache/pakfile.cpp:48:52: error: no matching function for call to 'zipper::Unzipper::extractEntryToMemory(const string&, utils::Bytes&)'
unzipper_->extractEntryToMemory(fileName, bytes);
^
In file included from /resourcecache/pakfile.hpp:13:0,
from /resourcecache/pakfile.cpp:1:
/projects/linux/../../thirdparty/zipper/zipper/unzipper.h:31:10: note: candidate: bool zipper::Unzipper::extractEntryToMemory(const string&, std::vector<unsigned char>&)
bool extractEntryToMemory(const std::string& name, std::vector<unsigned char>& vec);
^~~~~~~~~~~~~~~~~~~~
/projects/linux/../../thirdparty/zipper/zipper/unzipper.h:31:10: note: no known conversion for argument 2 from 'utils::Bytes {aka std::vector<std::byte>}' to 'std::vector<unsigned char>&'
Я попытался выполнить наивные броски, но это тоже не помогает. Итак, если он предназначен для того, чтобы быть полезным, будет ли он действительно полезным в старых контекстах? Единственный метод, который я вижу, - использовать std::transform
для использования нового вектора байтов в этих местах:
utils::Bytes bytes;
std::vector<unsigned char> rawBytes;
unzipper_->extractEntryToMemory(fileName, rawBytes);
std::transform(rawBytes.cbegin(),
rawBytes.cend(),
std::back_inserter(bytes),
[](const unsigned char c) {
return static_cast<std::byte>(c);
});
return bytes;
Что есть:
- Гадкий.
- Делает много бесполезных строк (может быть перезаписано, но все же это нужно написать до:)).
- Копирует память вместо использования уже созданного фрагмента
rawBytes
.
Итак, как использовать его в старых местах?
Ответы
Ответ 1
Вы упускаете момент, почему std::byte
был изобретен в первую очередь. Причина, по которой он был изобретен, состоит в том, чтобы хранить в памяти необработанный байт, не предполагая, что это символ. Вы можете увидеть это в cppreference.
Подобно char и unsigned char, его можно использовать для доступа к необработанной памяти, занятой другими объектами (представление объектов), но в отличие от этих типов, это не символьный тип и не арифметический тип.
Помните, что C++ является строго типизированным языком в интересах безопасности (поэтому неявные преобразования во многих случаях ограничены). Значение: если бы неявное преобразование из byte
в char
было возможно, это бы победило цель.
Итак, чтобы ответить на ваш вопрос: чтобы использовать его, вы должны разыграть его, когда захотите сделать ему задание:
std::byte x = (std::byte)10;
std::byte y = (std::byte)'a';
std::cout << (int)x << std::endl;
std::cout << (char)y << std::endl;
Все остальное не должно работать, по замыслу! Согласитесь, что преобразование некрасиво, но если вы хотите хранить символы, используйте char
. Не используйте байты, если вы не хотите хранить необработанную память, которая не должна интерпретироваться как char
по умолчанию.
А также последняя часть вашего вопроса, как правило, неверна: вам не нужно делать копии, потому что вам не нужно копировать весь вектор. Если вам временно нужно прочитать byte
как char
, просто static_cast
его в том месте, где вам нужно использовать его как char
. Это ничего не стоит и безопасно для типов.
Что касается вашего вопроса в комментарии о приведении std::vector<char>
к std::vector<std::byte>
, вы не можете этого сделать. Но вы можете использовать необработанный массив внизу. Итак, следующий тип имеет тип (char*)
: std::vector<std::byte> bytes;
// fill it...
char* charBytes = reinterpret_cast<char*>(bytes.data());
Это имеет тип char*
, который является указателем на первый элемент вашего массива, и может быть разыменован без копирования следующим образом:
std::cout << charBytes[5] << std::endl; //6th element of the vector as char
И размер вы получаете от bytes.size()
. Это верно, так как std::vector
непрерывен в памяти. Обычно вы не можете сделать это с любым другим контейнером std (deque, list и т.д.).
Несмотря на то, что это действительно, это устраняет часть безопасности из уравнения, имейте это в виду. Если вам нужен char
, не используйте byte
.
Ответ 2
Если ваш код старого стиля принимает диапазоны или итераторы в качестве аргументов, вы можете продолжать использовать их. В тех немногих случаях, когда вы не можете (например, explicit
конструкторы, основанные на диапазоне), вы можете теоретически написать новый класс итератора, который оборачивает итератор в unsigned char
и преобразует *it
в std::byte&
.
Ответ 3
Если вы хотите что-то, что ведет себя как байт так, как вы, вероятно, ожидаете, но имя явно отличается от знака без знака, используйте uint8_t из stdint.h. Почти для всех реализаций это, вероятно, будет
typedef unsigned char uint8_t;
и снова неподписанный символ под капотом - но кого это волнует? Вы просто хотите подчеркнуть "Это не тип персонажа". Вам просто не нужно ожидать, что вы сможете иметь две перегрузки некоторых функций: одну для неподписанного символа и одну для uint8_t. Но если вы сделаете это, то компилятор все равно засунет в него нос...