Почему uint8_t и int8_t не работают с файловыми и консольными потоками?

$ file testfile.txt
testfile.txt: ASCII text

$ cat testfile.txt 
aaaabbbbccddef

#include <iostream>
#include <fstream>
#include <string>
#include <cstdint>
typedef uint8_t byte; // <-------- interesting
typedef std::basic_ifstream<byte> FileStreamT;
static const std::string FILENAME = "testfile.txt";
int main(){
    FileStreamT file(FILENAME, std::ifstream::in | std::ios::binary);
    if(!file.is_open())
        std::cout << "COULD NOT OPEN FILE" << std::endl;
    else{
        FileStreamT::char_type buff;
        file.read(&buff,1);
        std::cout << (SOMECAST)buff; // <------- interesting
    }
    std::cout << "done" << std::endl;
}

В зависимости от того, что находится в typedef и к чему он приведен (или не приведен), он делает всякие глупости.

Это работает с 'typedef char' и без приведения. (97 при приведении к int, как и ожидалось)

И uint8_t, и int8_t будут печатать

  • ничего без актеров

  • ничего, когда приведено к символу или без знака

  • 8 при приведении к int или unsigned (хотя ASCII 'a' должно быть 97)

Мне каким-то образом удалось напечатать символ " ", но я забыл, в каком случае это было.

Почему я получаю эти странные результаты?

заметки для будущего читателя:

Вывод из ответа: только создавайте экземпляры потоков с символом (или одним из широких символов, упомянутых в стандарте), в противном случае вы не получите предупреждение компилятора и тихий сбой

очень грустно, что стандарт гарантирует эти вещи

Мораль этой истории: избегать C++

1 ответ

Решение

Декларация template std::basic_ifstream является:

template< 
    class CharT, 
    class Traits = std::char_traits<CharT>
> class basic_ifstream;

Стандарт C++03 (21.1/1) требует, чтобы библиотека определяла специализации std::char_traits<CharT> за CharT знак равно char, wchar_t,

Стандарт C++11 (C++11 21.2/1) требует, чтобы библиотека определяла специализации std::char_traits<CharT> за CharT знак равно char,char16_t,char32_t,wchar_t,

Если вы создаете экземпляр std::basic_ifstream<Other> с Other ни один из 2[4] типов, назначенных Стандартом, к которому вы компилируете, поведение будет неопределенным, если вы сами не определите my_char_traits<Other> как вам требуется, а затем создать экземплярstd::basic_ifstream<Other,my_char_traits<Other>>,

Продолжение в ответ на комментарии ОП.

Запрос std::char_traits<Other> не вызовет ошибок при создании экземпляра шаблона: шаблон определен так, что вы можете его специализировать, но использование по умолчанию (не специализированное) может привести к ошибкам Other или действительно для любого данного CharTгде неправильные средства не соответствуют требованиям Стандарта для класса черт характера в соответствии с C++03 § 21.1.1 / C++11 § 21.2.1.

Вы подозреваете, что typedef может помешать выбору специализации шаблона для typedefтип, то есть тот факт, что uint8_t а также int8_tявляются typedefs для основных типов символов может привести к std::basic_ifstream<byte>не то же самое, что std::basic_ifstream<FCT>где FCT- псевдоним основного типа символа.

Забудь об этом подозрении.typedef прозрачный Кажется, вы верите одному из typedefs int8_t а также uint8_t должно быть char, в этом случае - если typedef как-то мешает разрешению шаблона - одно из неправильных действий basic_ifstream проверенные вами экземпляры должны быть std::basic_ifstream<char>

Но как насчет того, что typedef char byte безвреден? Это убеждение, что либо int8_t или же uint8_t знак равно char ложно Вы найдете это int8_t это псевдоним для signed char в то время как uint8_t это псевдоним для unsigned char, Но ни signed char ни unsigned char тот же тип, что и char:

C++03/11 § 3.9.1 / 1

Обычный символ, подписанный символ и неподписанный символ - это три различных типа

Так что оба char_traits<int8_t> а также char_traits<uint8_t> по умолчанию, неспециализированные, экземпляры шаблона char_traits и вы не имеете права ожидать, что они отвечают требованиям Стандарта в отношениичерт характера.

Один тестовый случай, в котором вы не обнаружили никакого плохого поведения, был для byte знак равно char, Это потому char_traits<char> является стандартной специализацией, предоставляемой библиотекой.

Связь между всем плохим поведением, которое вы наблюдали, и типами, которые вы заменили SOMECAST в:

std::cout << (SOMECAST)buff; // <------- interesting

нет Так как ваш тестовый файл содержит текст ASCII, basic_ifstream<char>это единственный экземпляр basic_ifstream что Стандарт гарантирует его чтение. Если вы читаете файл с помощью typedef char byte в вашей программе ни одно из приведенных вами приведений не даст неожиданного результата: SOMECAST знак равно char или же unsigned char будет выводить a, а такжеSOMECAST знак равно int или же unsigned int будет выводить 97,

Все плохое поведение возникает в результате basic_ifstream<CharT> с CharTнекоторый тип, который Стандарт не гарантирует.

Другие вопросы по тегам