Как выкинуть этот объект без сращивания, основываясь на условии? - PullRequest
4 голосов
/ 12 февраля 2020

Я наткнулся на, вероятно, тривиальную проблему. Я хочу вызвать doSomething() для экземпляра AbstractParser, где указанный экземпляр имеет производный класс FooParser или BarParser. Будет ли это один или другой, зависит от аргументов времени выполнения, предоставленных пользователем. Оставшаяся часть кода должна иметь только способ доступа к родительскому объекту AbstractParser. См. Ниже.

AbstractParser& myParser;   // Error: can't have a reference to nothing
if(some_condition){
    FooParser parser = FooParser();
    myParser = parser;
}else{
    BarParser parser = BarParser();
    myParser = parser;
}
// lots of code with other things ...
parser.doSomething();

Исходя из условия, я хочу использовать myParser (или указатель на него) вне области действия if блока, а myParser должен вести себя как соответствующий производный объект. Я хочу избежать копирования моего объекта и (если возможно) конструктора перемещения. Без условия я мог бы сделать это

FooParser parser = FooParser();
AbstractParser &myParser = parser;     // upcast without slicing
// lots of code with other things ...
parser.doSomething();

И это было бы чисто и элегантно, с удручающей скоростью без нарезки parser. Я не уверен, как это сделать сейчас.

Как я могу выгружать свои производные парсеры, не копируя созданные объекты или не выводя их go из области видимости? Нужно ли создавать std::unique_ptr<AbstractClass> и использовать make_unique<FooParser>() внутри if? Или я должен использовать функцию, которая возвращает ссылку на производный класс? Или static_cast<AbstractClass>? Или я это слишком усложняю?

Ответы [ 4 ]

4 голосов
/ 12 февраля 2020

Простое решение - использовать unique_ptr:

std::unique_ptr<AbstractParser> myParser;
if (some_condition)
    myParser = std::make_unique<FooParser>();
else
    myParser = std::make_unique<BarParser>();

myParser->doSomething();

Если вы хотите избежать выделения кучи, вам нужно использовать что-то вроде std::variant, но это не так удобно:

std::variant<std::monostate, FooParser, BarParser> var;
AbstractParser *myParser;
if (x)
    myParser = &var.emplace<FooParser>();
else
    myParser = &var.emplace<BarParser>();

myParser->doSomething();
1 голос
/ 12 февраля 2020

Самый простой способ сделать это - извлечь код для разделения методов:

class Foo {

    void subAction(AbstractParser& parser) { 
        parser.doSomething();
    }
    void action(bool some_condition) {
        if (some_condition) {
            FooParser foo;
            subAction(foo);
        } else {
            BarParser bar;
            subAction(bar);
        }
    }
};

Обратите внимание, что куча не используется.

1 голос
/ 12 февраля 2020

Вы можете сделать следующее:

std::variant<std::monostate, FooParser, BarParser> parser;
if(some_condition) {
    parser = FooParser();
} else {
    parser = BarParser();
}

std::visit([](auto & p) {
    if constexpr(std::is_same_v<std::decay_t(decltype(p)), std::monostate>) { 
    } else { 
         p.doSomething();
    } 
} ,parser);

Я думаю, что это может даже исключить динамическую c диспетчеризацию для вызова функции.

0 голосов
/ 12 февраля 2020

Вы можете абстрагировать использование парсера от функции и затем вызвать его. Таким образом, с:

AbstractParser& myParser;
if (condition()) {
    FooParser parser = FooParser();
    stuff_1();
    myParser = parser;
} else {
    BarParser parser = BarParser();
    stuff_2();
    myParser = parser;
}
stuff_3(myParser);

Кому:

([](auto&& use_parser){
    if (condition()) {
        FooParser parser = FooParser();
        stuff_1();
        use_parser(parser);
        return;
    } else {
        BarParser parser = BarParser();
        stuff_2();
        use_parser(parser);
        return;
    }
})([](AbstractParser& myParser) {
    stuff_3(myParser);
});

Может быть проще с совершенно отдельной функцией (например, просто stuff_3 будет работать в этом случае, так что вам не нужно любые лямбды), но лямбды позволяют вам захватывать вещи, поэтому их не нужно слишком сильно переделывать.

...