Будут ли функции conteval разрешать параметры шаблона в зависимости от аргументов функции?

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

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

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

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

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

Ответ 1

Нет.

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

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

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

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

Ответ 2

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

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;
}

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

Ответ 3

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

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

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

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

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

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

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

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