Будут ли функции conteval разрешать параметры шаблона в зависимости от аргументов функции? - PullRequest
52 голосов
/ 14 мая 2019

В C ++ 17 этот код недопустим:

constexpr int foo(int i) {
    return std::integral_constant<int, i>::value;
}

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

В C ++ 20 у нас будут consteval функции, которые необходимо оценивать во время компиляции, поэтому ограничение времени выполнения должно быть снято. Означает ли это, что этот код будет законным?

consteval int foo(int i) {
    return std::integral_constant<int, i>::value;
}

Ответы [ 3 ]

33 голосов
/ 14 мая 2019

Нет.

Какие бы изменения ни повлекла за собой бумага, которая в этот момент мала , она не может изменить тот факт, что определение не шаблонной функции вводится только один раз.Более того, если предложенный вами код будет допустимым, мы могли бы найти способ объявить переменную типа std::integral_constant<int, i>, что кажется слишком запретным с точки зрения ODR.

В документе также указано, что параметры не предназначены для обработки в качестве основных константных выражений в одном из его примеров;

consteval int sqrsqr(int n) {
  return sqr(sqr(n)); // Not a constant-expression at this  point,
}                     // but that's okay.

Короче говоря, параметры функции никогда не будут постоянными выражениями из-завозможное несоответствие при наборе текста.

24 голосов
/ 14 мая 2019

Означает ли это, что этот код будет законным?

consteval int foo(int i) {
    return std::integral_constant<int, i>::value;
}

Нет.Это все еще плохо сформировано.Хотя consteval требует, чтобы сам вызов был константным выражением, поэтому вы знаете, что аргумент, который создает i, должен быть константным выражением, но foo сам по себе все еще не является шаблоном.Шаблон?

Небольшое изменение в вашем примере может сделать это более очевидным:

consteval auto foo(int i) {
    return std::integral_constant<int, i>();
}

Если бы это было допустимо, foo(1) и foo(2) ... вернули бы разные типы.Это совершенно другая языковая функция ( параметры функции constexpr ) - потому что для того, чтобы это работало, такие функции действительно должны вести себя как шаблоны.

Это может показаться немного не интуитивным.В конце концов, если аргумент, который выдает i, является константным выражением, то, конечно, i должен быть также пригоден как единое целое?Но это все еще не так - в [expr.const] нет дополнительных исключений, которые разрешают параметры для непосредственных функций.Непосредственная функция по-прежнему является просто функцией, а ее параметры по-прежнему не являются константными выражениями - точно так же, как параметры обычной функции constexpr не являются константными выражениями.

Конечно, с int мы можем просто переписать функцию, чтобы поднять параметр функции в параметр шаблона:

template <int i>
consteval int foo() {
    return std::integral_constant<int, i>::value;
}

И C ++ 20 дает нам типы классов в качестве параметров шаблона нетипичного типа.Таким образом, мы можем сделать это для гораздо большего числа типов, чем могли бы раньше.Но все еще есть много типов, которые мы могли бы использовать в качестве параметра для непосредственной функции, которую мы не можем использовать в качестве параметра шаблона - так что это не всегда будет работать (например, std::optional или, что более интересно в C ++ 20, std::string).

8 голосов
/ 14 мая 2019

Казалось бы, это не будет законным в C ++ 20. Хорошее объяснение того, почему это было бы проблематично поддерживать, уже дано в ответах @Barry и @Columbo (на самом деле это не работает с системой типов). Я просто добавлю то, что я считаю соответствующими цитатами из стандарта, которые фактически делают это незаконным.

На основе [temp.arg.nontype] / 2

A шаблон-аргумент для нетипового шаблон-параметра должен быть преобразованным константным выражением […]

Преобразованное константное выражение - это константное выражение, которое неявно преобразуется в определенный тип [expr.const] / 7 (здесь тип параметра шаблона). Итак, ваш вопрос сводится к вопросу о том, является ли переменная внутри функции conteval константным выражением. Основано на [expr.const] / 8

Постоянное выражение - это либо ключевое постоянное выражение glvalue, которое относится к объекту, являющемуся разрешенным результатом постоянного выражения (как определено ниже), либо базовое постоянное выражение prvalue, значение которого удовлетворяет следующим ограничениям: […]

Выражение i является glvalue id-выражением , которое является основным константным выражением (поскольку его оценка не выполняет ничего из перечисленных в [expr.const] / 4 ). Однако объект, на который ссылается это основное константное выражение, не является разрешенным результатом константного выражения [expr.const] / 8 :

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

Рассматриваемый объект не имеет статической длительности хранения и не является временным объектом.

...