Подтвердить что ты не робот

Как преобразовать std::vector <unsigned char> в вектор <char> без копирования?

Мне не удалось найти этот вопрос, и это реальная проблема, с которой я сталкиваюсь.

У меня есть утилита загрузки файлов, которая возвращает std::vector<unsigned char>, содержащую содержимое всего файла. Однако для функции обработки требуется массив contiguos char (и что не может быть изменен - ​​это функция библиотеки). Так как класс, который использует функцию обработки, сохраняет копию данных в любом случае, я хочу сохранить его как vector<char>. Вот код, который может быть немного более наглядным.

std::vector<unsigned char> LoadFile (std::string const& path);

class Processor {
    std::vector<char> cache;
    void _dataOperation(std::vector<char> const& data);

public:
    void Process() {
        if (cache.empty())
            // here the problem!
            cache = LoadFile("file.txt");

        _dataOperation(cache);
    }
};

Этот код не компилируется, потому что (очевидно) нет соответствующего преобразования. Однако мы можем быть уверены, что временный вектор будет иметь одинаковую память (IOW sizeof(char) == sizeof(unsigned char))

Наивным решением было бы перебрать содержимое временного и бросить каждого персонажа. Я знаю, что в нормальном случае вызывается operator= (T&&).

В моей ситуации безопасно делать переинтерпретирование, потому что я уверен, что я буду читать только символы ASCII. В любом случае любой другой персонаж попадает в _dataOperation.

Итак, мой вопрос: как правильно и безопасно преобразовать временный вектор таким образом, который не требует копирования?

Если это невозможно, я предпочел бы безопасный способ копирования, а не небезопасную обработку. Я также мог бы изменить LoadFile, чтобы вернуть либо vector<char>, либо vector<unsigned char>.

4b9b3361

Ответ 1

В С++ 11, [basic.lval] p10 говорит,

Если программа пытается получить доступ к сохраненному значению объекта через значение gl другого, чем одно из следующих типов, поведение undefined:

  • ...
  • a char или неподписанный char тип.

(точное расположение может отличаться в других версиях С++, но смысл одинаков.)

Это означает, что вы можете взять vector<unsigned char> cache и получить доступ к его содержимому с помощью диапазона [reinterpret_cast<char*>(cache.data()), reinterpret_cast<char*>(cache.data()) + cache.size()). (@Kerrek SB упомянул об этом.)

Если вы сохраняете vector<unsigned char> в Processor, чтобы соответствовать типу возврата LoadFile, а _dataOperation() фактически принимает массив char (что означает const char* и размер), вы можете когда вы передаете аргумент _dataOperation()

Однако, если _dataOperation() принимает vector<char> специально, и вы сохраняете vector<unsigned char> cache, то вы не можете передать его reinterpret_cast<vector<char>&>(cache). (т.е. @André Puel совершенно не прав. Не слушайте его.) Это нарушает правила псевдонимов, и компилятор попытается разозлить ваших клиентов в 2 часа ночи. (И если эта версия вашего компилятора не справится с этим, следующая версия будет продолжать пытаться.)

Один из вариантов, как вы упомянули, имеет шаблон LoadFile() и возвращает (или заполняет) вектор нужного вам типа. Другим является копирование результата, для которого краткая версия снова является reinterpret_cast исходного вектора .data(). [basic.fundamental] p1 упоминает, что "для типов символов все биты представления объекта участвуют в представлении значений". Это означает, что вы не потеряете данные с помощью этого reinterpret_cast. Я не вижу твердой гарантии, что никакая битовая диаграмма unsigned char может вызвать ловушку, если reinterpret_cast'ed - char, но я не знаю ни о каком современном оборудовании или компиляторах, которые это делают.