std::is_constant_evaluated поведение

GCC9 уже реализует std::is_constant_evaluated. Я немного поиграл с ним и понял, что это довольно сложно. Вот мой тест:

constexpr int Fn1()
{
  if constexpr (std::is_constant_evaluated())
    return 0;
  else
    return 1;
}

constexpr int Fn2()
{
  if (std::is_constant_evaluated())
    return 0;
  else
    return 1;
}

int main()
{
  constexpr int test1 = Fn1(); // Evaluates to 0
  int test2 = Fn1();           // Evaluates to 0
  int const test3 = Fn1();     // Evaluates to 0

  constexpr int test4 = Fn2(); // Evaluates to 0
  int test5 = Fn2();           // Evaluates to 1
  int const test6 = Fn2();     // Evaluates to 0
}

По этим результатам я сделал следующие выводы:

  • if constexpr (std::is_constant_evaluated()) всегда оценивает ветвь true. Поэтому нет смысла использовать эту конструкцию.

  • Если компилятор оценивает переменную во время компиляции, std::is_constant_evaluated()) равно true, независимо от того, аннотирована ли эта переменная явно constexpr или нет.

Я прав?


person metalfox    schedule 18.01.2019    source источник
comment
Какие заголовки и параметры компилятора вы использовали для этого?   -  person P.W    schedule 18.01.2019
comment
@P.W заголовок <type_traits> и параметр компилятора -std=c++2a   -  person metalfox    schedule 18.01.2019


Ответы (2)


if constexpr требует постоянного выражения для условия. Так что is_constant_evaluated, конечно, всегда будет истинным в таком контексте.

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

person StoryTeller - Unslander Monica    schedule 18.01.2019
comment
могут ли быть пути кода в функции constexpr, которые нельзя оценить во время компиляции? Мне еще столькому предстоит научиться :) - person 463035818_is_not_a_number; 18.01.2019
comment
@ user463035818 constexpr означает, что у этой функции существуют аргументы, которые могут разрешить оценку во время компиляции. Это не означает, что эта функция всегда будет оцениваться во время компиляции. Это означает, например, std::abs может быть constexpr: при вызове с constexpr аргументами его можно оценить во время компиляции, но его можно использовать как обычно во время выполнения. - person Max Langhof; 18.01.2019
comment
@MaxLanghof, но можете ли вы на самом деле реализовать функцию constexpr с логикой, которая никогда не может быть выполнена во время компиляции? Думаю, именно это имел в виду user463035818. Я тоже удивлен. Вещь для шаблона функции явно отличается. Пример, который я считаю уместным - у вас никогда не может быть функции constexpr, которая вернет std::string, независимо от того, где и как вы ее вызовете. - person Fureeish; 18.01.2019
comment
@MaxLanghof а, спасибо, понял. Если вы передаете аргументы, не относящиеся ко времени компиляции, нет смысла требовать, чтобы функция могла быть оценена во время компиляции. - person 463035818_is_not_a_number; 18.01.2019
comment
Я ожидал, что компилятор оптимизирует путь кода постоянной оценки при создании версии функции во время выполнения, что в любом случае может произойти. Так что if constexpr имел для меня смысл. - person metalfox; 18.01.2019
comment
@metalfox - я понимаю. Поначалу это немного нелогично. Дело в том. Функция сообщает вам, когда что-то оценивается. Поэтому, если вы поместите это в контекст, который можно оценить только в определенное время... - person StoryTeller - Unslander Monica; 18.01.2019

Вот что я думаю об этом, может быть, вы найдете это полезным... а может и нет. Обратите внимание, что я думаю, что запись if constexpr (std::is_constant_evaluated()) будет очень распространенной ошибкой, и в эту ловушку легко попасть. Но, надеюсь, компиляторы просто диагностируют этот случай. Отправлено 91428, что исправлено для gcc 10.1.


По сути, у нас есть два разных правила для кода — типичные правила для обычного кода времени выполнения и ограничения для константных выражений, которые предназначены для constexpr программирования. Это ограничения expr.const: нет UB, нет reinterpret_cast и т. д. Эти ограничения постоянно уменьшаются от языкового стандарта к языковому стандарту, и это здорово.

По сути, поток управления (с точки зрения пути кода) чередуется между режимом «полного выполнения» и режимом constexpr. Как только мы входим в режим constexpr (путем инициализации объекта constexpr, оценки параметра шаблона или...), мы остаемся в нем до тех пор, пока не закончим... а затем возвращаемся в режим полного выполнения.

Что делает is_constant_evaluated(), так это просто: я в режиме constexpr? Он сообщает вам, находитесь ли вы в контексте, который требует постоянных выражений.

В этом представлении давайте посмотрим на if constexpr (is_constant_evaluated()). Независимо от того, в каком состоянии мы были раньше, if constexpr требует постоянного выражения при инициализации, поэтому это переводит нас в режим constexpr, если мы еще не были в нем. Следовательно, is_constant_evaluated() просто верно - безоговорочно.

Однако для if (is_constant_evaluated()) простой if не изменяет наше состояние между средой выполнения и constexpr. Таким образом, значение здесь зависит от контекста, из которого оно было вызвано. Инициализация test4 переводит нас в режим constexpr, потому что это объект constexpr. Во время его инициализации мы следуем правилам константных выражений... так что is_constant_evaluated()истинно. Но как только мы закончим, мы вернемся к правилам времени выполнения... поэтому при инициализации test5 is_constant_evaluated() равно false. (И тогда test6 является неудачным частным случаем языка — вы можете использовать константные целочисленные переменные в качестве константных выражений, поэтому мы одинаково относимся к их инициализации для этих целей.)

person Barry    schedule 18.01.2019
comment
Спасибо за подробное объяснение. Я в порядке с делом test6. Вполне логично так себя вести. Я нахожу случай if constexpr более нелогичным. - person metalfox; 18.01.2019
comment
@metalfox Я подозреваю, что это будет очень распространенное недоразумение. Я добавлю примечание к ответу на этот счет - person Barry; 18.01.2019
comment
GCC 10+ предупредит о случае if constexpr: gcc.gnu. org/ml/gcc-patches/2019-08/msg01841.html - person metalfox; 28.08.2019
comment
Случай с константными целыми числами еще более странный, поскольку они могут быть константными выражениями, но не обязательно. (Конечно, любую динамическую инициализацию можно выполнить статически, но обычно это незаметно.) - person Davis Herring; 29.08.2019
comment
GCC 91428 говорит, что is_constant_evaluated, вероятно, будет удален. Чем он будет заменен? - person Johannes Schaub - litb; 29.12.2019
comment
@Johannes Это не удаляется. - person Barry; 29.12.2019