Законно ли использовать побочные эффекты в исключениях, выдаваемых 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)

Так что это работает с текущими компиляторами, но законно ли это C ++ 11?

15

Решение

Это законно.

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

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

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

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

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

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

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

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

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

14

Другие решения

Других решений пока нет …