Безопасно ли обрабатывать std :: option в теле функции noexcept? - PullRequest
0 голосов
/ 24 сентября 2019

Я хочу использовать std :: option в качестве типа Maybe, и я обеспокоен, могу ли я использовать его, чтобы указать на потенциальный сбой в некоторых вычислениях, поэтому наблюдается как пустой параметр.

Например,рассмотрим:

// This function doesn't have state
std::optional<int> multiply_by_2(std::optional<int> ox) noexcept
{
    if (ox)
        return {ox.value() * 2};

    // empty input case
    return {};
}

int main()
{
    auto input_handle = get_stdin().lock();
    auto output_handle = get_stdout().lock();

    // This can panic the application, which is fine at this point
    // so just unwrap the optional and call it a day
    output_handle.println("Your marvellous calculation: {}", multiply_by_2(input_handle.get_line().as_int()).value());
}
  • Является ли это эффективным методом возврата вычислений?Может ли это избежать раздувания исключений?
  • Может ли это вызвать какие-либо проблемы?

Ответы [ 2 ]

3 голосов
/ 24 сентября 2019

Здесь, кажется, есть несколько вопросов.

1.Безопасно ли обрабатывать std::optional в теле функции noexcept?

Да, если тип T, заключенный в std::optional, не является копируемым.

В этом коде:

if (ox)
    return {ox.value() * 2};

, поскольку вы проверяете перед вызовом std::optional::value(), исключение никогда не будет выдано.При этом, поскольку вы уже уверены, что ox.has_value(), лучше использовать operator*:

if (ox)
    return {*ox * 2};

Таким образом, компилятору не нужно будет генерировать проверку предусловия и *Оператор 1022 * (в простых случаях компилятор может оптимизировать это на основе if (ox), но зачем заставлять компилятор работать больше).

2.Это эффективный метод для возврата вычислений?Может ли это избежать некоторого раздувания исключений?

Предположительно, под "раздуванием исключений" вы подразумеваете издержки двоичного размера из всего кода обработки исключений, который должен сгенерировать компилятор.Если по какой-либо причине вы действительно заботитесь об этом, тогда да - техника std::optional может избежать этого раздува за счет увеличения накладных расходов, когда не возникает ошибок.

Это компромисс, который вы должны принять с этимстиль обработки ошибок (std::optional, std::error_code, результат и т. д.).Вы соглашаетесь с постоянными накладными расходами на успех, чтобы получать постоянные накладные расходы на неудачу.Исключения, с другой стороны, приводят только к накладным расходам (недетерминированным во времени и пространстве) при сбое.

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

3.Может ли это вызвать какие-либо проблемы?

Основной проблемой здесь является отсутствие какой-либо информации об ошибке.std::optional не может передать причину сбоя операции.В обычном приложении это может не быть проблемой, но как только вы начнете составлять более сложные операции, проблемы с отслеживанием причины сбоя станут очевидными.

Даже в этом коде есть несколько состояний ошибкиэто может быть полезно для надлежащего сообщения: ошибки ввода-вывода и ошибки синтаксического анализа.И затем среди ошибок синтаксического анализа:

  • ввод может быть недопустимым числом;
  • число может превышать диапазон int.

Предполагая, что вы в конечном итоге не используете исключения, рассмотрите возможность использования чего-то вроде предложенной std :: Ожидается или Библиотека результатов .

Я бы не рекомендовал просто использовать std::variant (по крайней мере, не оборачивая его), поскольку он не передает намерения обработки ошибок.Также не поддерживается удержание void.

1 голос
/ 24 сентября 2019

Исключения - деликатная тема в C ++ с несколькими различными мнениями по этому поводу.Я знаю, что в полете есть предложения по стандартизации (c ++ 23 или более поздние), чтобы улучшить их, и вы правы, что они не ноль накладных расходов.

Однако ни одна система не будет полностьюноль накладных расходов, так как вам нужно обрабатывать ошибки и проверять их в нескольких местах.

Мне кажется, что использовать std :: option неправильно, потому что там нет никакой информации: она пошла не так, пожалуйста, присоедините отладчик, чтобы найтипочему.Я больше за исключение, подобное структуре, которая имеет значение или информацию об ошибке.std:: variant приходит на ум.Конечно, не сообщая об этом, вам все еще нужен отладчик, чтобы понять, что пошло не так.

Тем не менее, если вы не возражаете против этого, ваш код подойдет для функциональности и производительности.Что мне действительно интересно, так это то, что вам нужно использовать опционально в качестве аргумента?В настоящее время вы соединяете цепочку один за другим, и с перегрузкой функции вы можете обеспечить меньшую реализацию:

// This function doesn't have state
 int multiply_by_2(int ox) noexcept
{
    return ox * 2;
}

// This function doesn't have state
std::optional<int> multiply_by_2(std::optional<int> ox) noexcept
{
    if (ox)
        return multiply_by_2(ox.value());

    // empty input case
    return std::nullopt;
}

Таким образом, у вас будет меньше накладных расходов, если ваш ввод не может быть ошибкой.

Наконец: убедитесь, что эти методы видны вызывающей стороне.Поскольку вы не проверяете свой путь ошибки, в случае сбоя у вас будет UB.Я не проверял, хотя компиляторы могли бы удалить путь ошибки через вашу функцию, поскольку он не может быть использован в подтверждающей программе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...