Почему constexpr работает для нечистых функций

просматривая один из моих старых вопросов на constexpr, я наткнулся на очень (ИМХО) важный комментарий.
В основном это сводится к:
(это законно C ++ 11 :()

 constexpr double f(bool b)
{
return b? 42:42/(rand()+1); // how pure is rand ;)
}

Мой вопрос в том, что является причиной этого стандарта. Так как я большой поклонник ссылочной прозрачности, я надеюсь, что у них есть веская причина :), и я хотел бы знать это.

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

11

Решение

В стандарте соответствующее требование скрыто под основным списком требований для constexpr функции. Это в §7.1.5 / 5:

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

§5.19 определяет требования к константным выражениям, так что вы не можете вызвать rand(),

Расслабленное ограничение позволяет вам иметь функции, которые являются условно чистыми. Ваш пример f(true) является допустимым аргументом шаблона, но f(false) не является.

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

Ах, Литб ответ тоже правильно. (Но этот сформулирован проще.)

9

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

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

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

Пример,

constexpr int add(int a, int b) { return a + b; } //pure!

const int a = 2, b = 3; //const
int c = 2, d = 3;       //non-const

//we may read update c and d here!

const int v1 = add(2,3);  //computed at compile-time
const int v2 = add(a,3);  //computed at compile-time
const int v3 = add(2,b);  //computed at compile-time
const int v4 = add(a,b);  //computed at compile-time

const int v3 = add(c,3);  //computed at runtime
const int v3 = add(c,b);  //computed at runtime
const int v3 = add(a,d);  //computed at runtime
const int v3 = add(c,d);  //computed at runtime

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

7

Потому что для некоторой области входных параметров нечистый путь никогда не будет выбран. Для этого домена constexpr будет работать нормально.

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

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

6