Использование std::unique_ptr с std::istream?

Я пишу программу на С++, которая может получать данные из файла (переданного в качестве аргумента командной строки), из std::cin или std::istringstream. Он отлично работает и управляется с помощью std::istream*. Мне сказали, что это плохо, обработка необработанных указателей, поэтому я решил обернуть это в std::unique_ptr (т.е. std::unique_ptr). Проблема в том, что он не компилируется. Насколько я могу судить по ошибкам, std::istream защитил себя от использования для выделения памяти. Я пытался погуглить, но не думаю, что кто-то задавал подобный вопрос раньше (поскольку я видел только вопросы, относящиеся только к std::unique_ptr). Кто-нибудь знает, как этого добиться?

edit: errors: In file included from /usr/include/c++/4.8/iostream:40:0,
                 from /home/dtscode/Desktop/SLang/src/main.cpp:1:
/usr/include/c++/4.8/istream: In function ‘int main(int, char**)’:
/usr/include/c++/4.8/istream:606:7: error: ‘std::basic_istream<_CharT, _Traits>::basic_istream() [with _CharT = char; _Traits = std::char_traits<char>]’ is protected
       basic_istream()
       ^
compilation terminated due to -Wfatal-errors.
make[2]: *** [CMakeFiles/slang.dir/src/main.cpp.o] Error 1
make[1]: *** [CMakeFiles/slang.dir/all] Error 2
make: *** [all] Error 2

в ответ на эту строку: std::unique_ptr Stream(new std::istream());

Я также пробовал это без вызова конструктора istreams и без чего-либо в конструкторе unique_ptrs.

редактировать 2:

#include <iostream>
#include  <fstream>
#include  <sstream>
#include   <vector>
#include   <string>
#include   <memory>

#include <lexer.hpp>

int main(int argc, char *argv[]) {
    std::unique_ptr<std::istream> Stream(new std::istream());
    std::vector<std::string> Arguments(argv, argv + argc);

    switch(argc) {
        case 1:
            Stream = &std::cin;
            break;

        case 2:
            Stream = new std::ifstream(Arguments[1]);
            break;

        case 3:
            if(Arguments[1] == "-c") {
                Stream = new std::istringstream(Arguments[2]);
                break;
            }

        default:
            std::cerr<<"error: invalid arguments."<< std::endl;
            return 0;
    }

    Token::Lexeme CurrentToken = Token::Start;

    while(*Stream) {
        CurrentToken = getToken(Stream);
        lex_debug();
    }

    if(Stream != &std::cin) {
        delete Stream;
    }

    return 0;
}

person DTSCode    schedule 08.07.2014    source источник
comment
Мне сказали, что это плохо, обрабатывать необработанные указатели -- И вы спросили, почему? Какую причину назвали?   -  person Benjamin Lindley    schedule 09.07.2014
comment
да. они сказали, что опасно обрабатывать необработанные указатели и использовать решение stl   -  person DTSCode    schedule 09.07.2014
comment
Вам придется предоставить больше информации. std::istream защитил себя от использования для выделения памяти не имеет особого смысла.   -  person Praetorian    schedule 09.07.2014
comment
Вы должны были попросить их быть более конкретными. В любом случае, ваш вариант использования является прекрасным примером веской причины для использования необработанного указателя. То есть как невладеющая, переназначаемая ссылка. Если вам не нужно переназначать его, вы можете использовать ссылку, но код будет немного запутанным, поскольку ссылки должны быть связаны при создании.   -  person Benjamin Lindley    schedule 09.07.2014
comment
@BenjaminLindley Я бы так не сказал, не увидев кода. Я не могу сказать, каков их вариант использования на самом деле.   -  person Joseph Mansfield    schedule 09.07.2014
comment
Просто покажите нам код, пожалуйста (отредактируйте его в своем вопросе).   -  person 0x499602D2    schedule 09.07.2014
comment
Зачем понадобился istream*, почему бы просто не использовать std::istream   -  person Fantastic Mr Fox    schedule 09.07.2014
comment
@Ben: Потому что он пытается использовать полиморфизм во время выполнения.   -  person Benjamin Lindley    schedule 09.07.2014


Ответы (3)


Ошибка, которую вы показываете, связана с вызовом конструктора std::istream, это базовый класс и не может быть создан. Даже если код скомпилируется, std::unique_ptr является указателем-владельцем, поэтому при попытке удалить std::cin возникнут проблемы. Вам нужен указатель, не являющийся владельцем, поэтому правильным будет либо необработанный указатель, либо std::weak_ptr.

РЕДАКТИРОВАТЬ: я бы предложил вместо этого использовать ссылки, если это возможно.

person naraku9333    schedule 12.07.2014
comment
Базовые классы могут быть созданы. Абстрактные классы не могут. - person Lightness Races in Orbit; 21.07.2016

Я бы сказал, что это просто очень плохо факторизованный код. Почему бы не сделать так:

void process_input(std::istream & is);

int main(int argc, char * argv[]) {
    if (argc == 1) {
        process_input(std::cin);
    } else if (argc == 2) {
        std::ifstream is(argv[1], "rb");
        process_input(is);
    } else if (argc == 3 && strcmp(argv[1], "-c") == 0) {
        std::istringstream is(argv[2]);
        process_input(is);
    } else {
        std::cerr << "Unrecognized invocation.\n";
        return EXIT_FAILURE;
    }
}
person Kerrek SB    schedule 08.07.2014
comment
почему это лучше моего? - person DTSCode; 09.07.2014
comment
@DTSCode: а) он компилируется, б) в нем нет странной путаницы с владельцами, в) вы отделяете код драйвера от логики, г) код логики можно тестировать отдельно, д) меньше сложности, больше читаемости, е) единороги. - person Kerrek SB; 09.07.2014
comment
@BenjaminLindley: Только iostreams заставили меня хотеть иметь противоположность std::move, то есть template <typename T> T & stay(T && x) { return x; }. - person Kerrek SB; 09.07.2014
comment
хорошо, мне просто было интересно. однако, если я не помещу его в unique_ptr, он отлично работает/компилируется - person DTSCode; 09.07.2014
comment
@DTSCode: у меня есть личное мнение о коде, который работает нормально или работает идеально, но подходит вам. Это ваша кодовая база. - person Kerrek SB; 09.07.2014

Я часто выбираю потоки следующим образом:

#include <iostream>
#include <fstream>
#include <istream>
#include <sstream>
#include <vector>

int main(int argc, char *argv[])
{
    std::vector<std::string> args(argv, argv + argc);

    std::ifstream ifs(argc == 2 ? args[1]:"");
    std::istringstream iss(argc == 3 ? args[2]:"");

    std::istream& is = argc == 2 ? ifs : argc == 3 && args[1] == "-c" ? iss : std::cin;

    while(is)
    {
        // process stream
    }
}

Вот как я избегаю использования указателей.

person Galik    schedule 09.07.2014