Подтвердить что ты не робот

Является ли законным использовать побочные эффекты в исключениях, создаваемых constexpr?

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

#include <iostream>
#include <cstdlib>
#include <stdexcept>

struct constexpr_precond_violated : std::logic_error
{
  constexpr_precond_violated(const char* msg) :
    std::logic_error(msg)
  {
    std::cerr << msg << '\n';
    abort(); // to get a core dump
  }
};

#define TO_STRING_IMPL(x) #x
#define TO_STRING(x) TO_STRING_IMPL(x)

#define CONSTEXPR_PRECOND(cond, value) \
  ((!(cond)) ? throw constexpr_precond_violated( \
    "assertion: <" #cond "> failed (file: " \
    __FILE__ ", line: " TO_STRING(__LINE__) ")")    \
   : (value))

constexpr int divide(int x, int y)
{
  return CONSTEXPR_PRECOND(y != 0, x / y);
}

int main(int argc, char** argv)
{
  // The compiler cannot know argc, so it must be evaluated at runtime.
  // If argc is 2, the precondition is violated.
  return divide(100, argc - 2);
}

Я тестировал его с помощью g++ 4.7.2 и clang++ 3.1. Когда предварительные условия терпят неудачу, вы получаете местоположение ошибки и дамп ядра.

./constexpr_assert some_arg
assertion: <y != 0> failed (file: constexpr_assert.cpp, line: 26)
Aborted (core dumped)

Итак, он работает с текущими компиляторами, но является ли он законным С++ 11?

4b9b3361

Ответ 1

Это законно.

Для каждой функции constexpr должны быть некоторые значения аргументов, которые приводят к постоянному выражению (§7.1.5/5):

Для функции constexpr, если значения аргументов функции не существуют что подстановка функции вызовет постоянство выражение (5.19), программа плохо сформирована; не требуется диагностика.

Обратите внимание, что это не означает, что каждое возможное значение аргумента должно приводить к постоянному выражению. divide явно имеет некоторые значения аргументов, которые приводят к постоянному выражению: divide(1, 1) - простой пример. Таким образом, определение очевидно.

Но можно ли вызывать divide(1, 0)? Да, оно может. Практически нет никакой разницы между вызовом функции constexpr или "нормальной" функцией (§7.1.5/7):

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

Обратите внимание, что вызовы функций constexpr могут отображаться в постоянных выражениях, но ничто не запрещает им не приводить к постоянным выражениям. Это предназначено для вызова функций constexpr с аргументами времени компиляции и времени выполнения (в противном случае полезность constexpr будет несколько ограничена).

Для полноты, посмотрим, что делает постоянное выражение (§5.19/2):

Условное выражение является выражением постоянной константы, если оно включает одно из следующего в качестве потенциально оцениваемого подвыражения (§3.2), но подвыражения логического И (§5.14), логического ИЛИ (§5.15), и условные (§5.16) операции, которые не оцениваются, не являются считается [...].

Итак, divide(1, 1) является константным выражением, но divide(1, 0) не является. Если вы использовали divide(1, 0) в параметре шаблона, программа была бы плохо сформирована. Но в остальном это прекрасно.