if vs if constexpr внутри функции constexpr

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

template<int N>
constexpr bool is_negative()
{
    if constexpr  (N >= 0) return false;
    else  return true; 
}
int main()
{
    constexpr  bool v = is_negative<1>();
}

live demo

В приведенном выше случае N должен быть известен во время компиляции, потому что он не является параметром шаблона типа, поэтому if constexpr здесь отлично работает. Однако это функция constexpr, поэтому iirc возможно получить возвращаемое значение, даже если я заменяю if constexpr на if:

template<int N>
constexpr bool is_negative()
{
    if  (N >= 0) return false;
    else  return true; 
}
int main()
{
    constexpr  bool v = is_negative<1>();
}

live demo

Из cppref все требования в A constexpr function must satisfy the following requirements: не упоминают if. Итак, IIUC, поведение constexpr должно быть определено реализацией, содержит ли функция constexpr if для вычисления во время компиляции, даже если все связанные переменные известны во время компиляции (как is_negative выше).

Итак, мой вывод:

  • До c ++ 17 у нас не было if constexpr, поэтому выбор был if, что означает, что не гарантируется выполнение наших функций constexpr во время компиляции, все зависит от реализации компилятора.
  • После c ++ 17 предпочтительнее использовать if constexpr, если мы хотим, чтобы функции constexpr оценивались во время компиляции.

Все вышесказанное - мои личные мысли, возможно, что-то важное упущено / неправильно понято, не стесняйтесь поправлять меня. Вопрос по-прежнему не изменился: if и if constexpr, которые следует отдавать предпочтению для функций constexpr, которые, как ожидается, будут оцениваться во время компиляции.

ссылки: - Что разрешено в функции constexpr? - Разница между if constexpr () и if ()


person 陳 力    schedule 17.12.2018    source источник
comment
Вычисления констант всегда были вычислениями во время компиляции. Например. использование шаблона is_negative() с 13 создает условие 13 >= 0, и компилятор разрешит это значение до 1 (также без constexpr). Современные компиляторы оптимизируют все условия, но я не знаю, обязательно ли это, и если да, то с какой версии стандарта. Кстати. if (N >= 0) return true; else return false;: почему не return N >= 0;? Или это произошло из-за упрощения минимального воспроизводимого примера?   -  person Scheff's Cat    schedule 17.12.2018
comment
Ой, мой плохой, это плохой стиль. Я должен был опубликовать образец получше ...   -  person 陳 力    schedule 17.12.2018
comment
Хорошо, нет проблем. (Вы могли бы также сказать, что это произошло из-за MCVE. Такие вещи могут выглядеть немного глупо, но предназначены просто для демонстрации принципа. ИМХО, это приемлемо, если указано как таковое.) ;-)   -  person Scheff's Cat    schedule 17.12.2018
comment
@Scheff Я обнаружил, что просто сказал одному оператору, что это ужасный стиль 2 месяца назад stackoverflow.com/a/52826880/6949852 XD   -  person 陳 力    schedule 17.12.2018
comment
Я с @Nelfeal. Арифметические или реляционные операции с константами снова дают константы (оценка во время компиляции). Пытался найти соотв. часть в постоянных выражениях, но не удалось, пока я не понял, что основные постоянные выражения выражается в обратном порядке, т.е. они перечисляют все, что делает выражение не основным постоянным выражением. Мне нужно было немного понять это ... ;-)   -  person Scheff's Cat    schedule 17.12.2018


Ответы (2)


До c ++ 17 у нас не было if constexpr, поэтому выбор был if, что означает, что не гарантируется получение наших функций constexpr во время компиляции, все зависит от реализации компилятора.

Тот факт, что оператор if не является constexpr, не означает, что он не может быть оценен во время компиляции как часть выражения constexpr. В вашем примере v оценивается во время компиляции в обоих случаях, потому что это требуется: это постоянное выражение. Это не определено реализацией.

После c ++ 17, если constexpr предпочтительнее, если мы хотим, чтобы функции constexpr оценивались во время компиляции.

Constexpr if операторы были введены для решения проблемы. Получение оценки функций constexpr во время компиляции не является этой проблемой.

Вот пример, где требуется constexpr if вместо простого if (взято из cppreference):

template <typename T>
auto get_value(T t) {
    if constexpr(std::is_pointer_v<T>)
        return *t; // deduces return type to int for T = int*
    else
        return t;  // deduces return type to int for T = int
}

Попробуйте удалить ключевое слово constexpr и посмотрите, что произойдет (демонстрация).

Также обратите внимание, что вы всегда можете решить эту проблему, используя другие методы, но if constexpr имеет преимущество краткости. Например, вот эквивалент get_value с использованием диспетчеризации тегов:

template<typename T>
auto get_value_impl(T t, std::true_type) {
    return *t;
}
template<typename T>
auto get_value_impl(T t, std::false_type) {
    return t;
}

template<typename T>
auto get_value(T t) {
    return get_value_impl(t, std::is_pointer<T>{});
}

Демо

person Nelfeal    schedule 17.12.2018

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

constexpr bool is_negative(int n)
{
    if  (n >= 0) return false;
    else  return true; 
}
int main(int argc, char**)
{
    constexpr  bool v = is_negative(1);
    bool b = is_negative(argc);
    return static_cast<int>(v || b);
}

Для приведенного выше кода написать if constexpr не получится. Так же вы можете вызвать функцию со значением времени выполнения.

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

Реальный интерес if constexpr - это когда действителен только один путь:

template <typename T>
constexpr auto get_value(T t) {
    if constexpr(std::is_pointer_v<T>)
        return *t; // deduces return type to int for T = int*
    else
        return t;  // deduces return type to int for T = int
}

Если T будет int, путь кода с *t недействителен, так как вы не можете разыменовать int. Однако, поскольку вместо if используется if constexpr, код в ложном пути должен быть синтаксически правильным только в том случае, если он зависит от аргумента шаблона.

Поскольку вы ищете руководство, компилятор уже требует: Используйте if constexpr, если один из путей кода недействителен. Используйте if, когда зависит от аргументов.

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

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

person JVApen    schedule 17.12.2018