Немедленный вызов деструктора после настройки в свободном интерфейсе на С++

Я хочу создать класс только для установки параметров функции. Я использую свободный интерфейс для этого. Какая-то функция возвращает объект к заданным параметрам, и реальный код будет выполняться в деструкторе этого объекта.

Прежде всего, это хорошая идея? Это обычный способ улучшить читаемость кода, когда у меня много параметров? Или, может быть, я должен использовать другой способ сделать это? Я был бы рад проконсультировать по этому поводу.

Если все в порядке, как я могу предотвратить случайное сохранение объекта в локальную переменную? В этом случае деструктор будет вызываться в конце области видимости переменной.

Пример:

class MyClass {
public:
    ~MyClass() noexcept {
        // some code to execute immediately after setting params A and B
    }

    // abstract setting methods
    MyClass &SetA() { return *this; }
    MyClass &SetB() { return *this; }
};

MyClass SomeFunction() {
    return MyClass();
}

int main() {
    // Correct usage:
    SomeFunction()
            .SetA()
            .SetB();
    // destructor here

    // I want to prevent it:
    auto my_class = SomeFunction();
    my_class
            .SetA()
            .SetB();

    {
        auto my_class2 = SomeFunction();
        my_class2
                .SetA()
                .SetB();
        // destructor of `my_class2` here
    }

    // destructor of `my_class` here.
    return 0;
}

person User98    schedule 21.11.2020    source источник
comment
Честно говоря, это похоже на проблему XY.   -  person PaulMcKenzie    schedule 21.11.2020


Ответы (1)


В общем, деструктор — это хорошее место, если вы хотите, чтобы что-то произошло, даже если возникло исключение (но имейте в виду, что деструкторы объявлены noexcept(true) по умолчанию).

Я лично использую его для блоков finally (с noexcept(false)). Но обычно в C++ вам не нужно finally из-за RAII.

Я думаю, вы не можете предотвратить продление срока службы/локальные переменные. А атрибута [[discard]] у нас нет, только [[nodiscard]].

Но почему вы не выполняете работу в SetA()? Для меня это было бы более понятно?

Хорошей причиной было бы, если вам нужны все данные параметров для выполнения работы...

Обновлять

Есть один вариант. Вы можете сделать свой объект недоступным для копирования/перемещения:

    MyClass() = default;
    MyClass(const MyClass&) = delete;
    MyClass(MyClass&&) = delete;

Но с таким подходом у вас не может быть чего-то вроде SomeFunction. И, конечно же, такая фабричная функция может вызывать копию, которая также вызывает ваш деструктор.


В качестве предложения я бы просто сделал это явно:

SomeFunction()
            .SetA()
            .SetB()
            .Exec()
;
person Bernd    schedule 21.11.2020
comment
Я считаю, что нет никакой гарантии, что Exec() будет вызываться последним. - person PaulMcKenzie; 22.11.2020
comment
Нет, это гарантировано, что не гарантируется, так это порядок оценки параметров... - person Bernd; 22.11.2020
comment
Для справки. Возможно, обновите ответ, чтобы включить это. - person PaulMcKenzie; 22.11.2020