просматривая один из моих старых вопросов на constexpr, я наткнулся на очень (ИМХО) важный комментарий.
В основном это сводится к:
(это законно C ++ 11 :()
constexpr double f(bool b)
{
return b? 42:42/(rand()+1); // how pure is rand ;)
}
Мой вопрос в том, что является причиной этого стандарта. Так как я большой поклонник ссылочной прозрачности, я надеюсь, что у них есть веская причина :), и я хотел бы знать это.
Кстати, есть связанный Q, но большинство из A даже не упоминает чистую вещь, или когда они делают, они не указывают причины, по которым стандартное разрешение позволяет это.
Связь между constexpr и чистыми функциями
В стандарте соответствующее требование скрыто под основным списком требований для constexpr
функции. Это в §7.1.5 / 5:
Для функции constexpr, если не существует значений аргументов функции, таких, что подстановка вызова функции выдаст константное выражение (5.19), программа будет плохо сформирована; Диагностика не требуется.
§5.19 определяет требования к константным выражениям, так что вы не можете вызвать rand()
,
Расслабленное ограничение позволяет вам иметь функции, которые являются условно чистыми. Ваш пример f(true)
является допустимым аргументом шаблона, но f(false)
не является.
Недостатком, конечно, является то, что компилятор не будет проверять, что constexpr
Функция может фактически использоваться по прямому назначению. Вам нужно написать контрольные примеры.
Ах, Литб ответ тоже правильно. (Но этот сформулирован проще.)
Ключевое слово 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
является чистой функцией независимо от того, вычисляется ли она во время компиляции или во время выполнения.
Потому что для некоторой области входных параметров нечистый путь никогда не будет выбран. Для этого домена constexpr будет работать нормально.
Например, ваша функция может иметь простую ветвь и более сложную ветвь. И вы можете указать, что для того, чтобы ваша функция могла использоваться в константных выражениях, аргументы функции должны быть такими, чтобы выполнялось это и то условие, уступая простой ветви в вашей функции, которая всегда чиста.
Полезным побочным эффектом этого является то, что вы можете вызвать ошибки во время постоянных вычислений. Т.е. если нарушено предусловие в простой ветке, вы можете вызвать оценку нечистого выражения, что вызовет ошибку времени компиляции (здесь хорошее утверждение или исключение, так как он продолжает жаловаться, когда функция вызывается в контекст выполнения).