Как я могу выполнить утверждение во время выполнения в функции constexpr?

Насколько я понимаю, функция constexpr может выполняться как во время компиляции, так и во время выполнения, в зависимости от того, можно ли выполнить всю оценку во время компиляции или нет.

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

Итак, мой вопрос: как я могу добавить утверждение времени выполнения, чтобы гарантировать, что выполнение функции времени выполнения передается действительными параметрами вместе с моим static_assert?


person Adrian    schedule 04.09.2015    source источник


Ответы (2)


Эрик Ниблер хорошо освещает эту проблему в Assert и Constexpr в C++. 11, он указывает, что использование assert в функции constexpr не разрешено в C++11, но разрешено в C++14(В рамках предложения о смягчении ограничений для функций constexpr) и предоставляет следующий фрагмент :

constexpr bool in_range(int val, int min, int max)
{
    assert(min <= max); // OOPS, not constexpr
    return min <= val && val <= max;
}

Если нам нужно поддерживать C++11, то есть несколько альтернатив. Очевидным является использование throw, но, как он указывает, это превращает то, что должно быть неисправимой ошибкой, в исправимую, поскольку вы можете поймать исключение.

Он предлагает несколько альтернатив:

  1. Использование throw с спецификатором noexcept:

    constexpr bool in_range(int val, int min, int max) noexcept 
    {
      return (min <= max)
        ? min <= val && val <= max
        : throw std::logic_error("Assertion failed!");
    }
    

    если исключение уходит, будет вызвана функция std::terminate.

  2. Вызовите std::quick_exit из конструктора типа исключения:

    struct assert_failure
    {
        explicit assert_failure(const char *sz)
        {
            std::fprintf(stderr, "Assertion failure: %s\n", sz);
            std::quick_exit(EXIT_FAILURE);
        }
    };
    
    constexpr bool in_range(int val, int min, int max)
    {
        return (min <= max)
          ? min <= val && val <= max
          : throw assert_failure("min > max!");
    }
    
  3. Передайте лямбда-выражение, которое утверждает конструктору типа исключения:

    constexpr bool in_range(int val, int min, int max)
    {
        return (min <= max)
          ? min <= val && val <= max
          : throw assert_failure(
              []{assert(!"input not in range");}
            );
    }
    
person Shafik Yaghmour    schedule 04.09.2015
comment
Я также обнаружил, что вы можете использовать assert непосредственно в контексте списка. constexpr bool in_range(int val, int min, int max) { return (assert(min <= max), min <= val && val <= max); } По сути, вы должны сделать так, чтобы он никогда не мог перейти к вызову, отличному от constexpr, если он используется в контексте constexpr в случае сбоя. Это работает, потому что assert является макросом для троичного выражения, которое оценивает вызов базовой функции, отличной от constexpr, в случае сбоя. - person Adrian; 09.09.2015
comment
@Adrian интересно отметить, что оператор запятой разрешен только в константных выражениях в C++11. - person Shafik Yaghmour; 09.09.2015
comment
@Adrian, хотя это не будет переносимым, поскольку оно основано на деталях реализации assert, которые не охвачены стандартом. - person Shafik Yaghmour; 09.09.2015

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

В этом вопросе показан пример кода, в котором это происходит: Передача объектов constexpr

Также связано: Что происходит при возникновении исключения при вычислении constexpr?

person Chris Beck    schedule 04.09.2015