Что определяет, является ли функция constexpr постоянным выражением?

Nov 30 2020

(используемый компилятор - это gcc с c ++ 17, насколько мне известно (сложно найти это в Visual Studio))

#include <iostream>

using namespace std;

void increment( int& v )
{
    ++v;
}

int constexpr f()
{
    int v = 0;
    increment( v );
    return v;
}

int main( )
{
    cout << f( ) << '\n';
}

Приведенный выше код дает ошибку при компиляции:

Функция constexpr 'f' не может привести к постоянному выражению.

Насколько я понимаю, это потому, что функция incrementне является constexpr. Меня смущает то, что следующий код компилируется нормально:

#include <iostream>

using namespace std;

void increment( int& v )
{
    ++v;
}

int constexpr f()
{
    int v = 0;
    for( int i = 0; i < 1; ++i )
    {
        increment( v );
    }   
    return v;
}

int main( )
{
    cout << f( ) << '\n';
}

Этот код функционально тот же, и он компилируется, хотя инкремент все еще не является constexpr. Я не понимаю, как это возможно, что цикл for в диапазоне [0, 1) заставляет компилятор понять, что функция на fсамом деле является constexpr.

Если кто-нибудь может дать некоторое представление о constexpr в С ++ и об этой очевидной несогласованности, я был бы очень признателен.

Ответы

12 aschepler Nov 30 2020 at 22:23

Обе программы «плохо сформированы, и диагностика не требуется», согласно [dcl.constexpr] / 6 :

Для функции constexpr или конструктора constexpr, который не является ни заданным по умолчанию, ни шаблоном, если не существует значений аргументов, так что вызов функции или конструктора мог бы быть оцененным подвыражением основного константного выражения или, для конструктора, оцененным подвыражением полное выражение инициализации некоторого инициализированного константой объекта ( [basic.start.static] ), программа имеет неправильный формат , диагностика не требуется.

Немного странно, что gcc просто не замечает проблемы со второй программой, но она все еще соответствует.

Обратите внимание, что диагностика потребовалась бы, если бы она fиспользовалась, например, в контексте, который фактически требует постоянного выражения constexpr int n = f();.

Некоторые вещи никогда не допускаются в функции constexpr. Для этого требуется диагностика (обычно сообщение об ошибке), даже если функция никогда не используется в постоянном выражении - см . Ответ cigien . Но программы, о которых идет речь, не нарушают ни одно из этих более строгих правил.

6 cigien Nov 30 2020 at 22:27

Поскольку вы не вызываете fконстантное выражение, ваш вопрос спрашивает, требуется ли компилятору диагностировать то, что fне может быть вызвано в константном выражении, исключительно на основе его определения .

Требования к определению в виде constexprфункций перечислены здесь :

Определение функции constexpr должно удовлетворять следующим требованиям:

(3.1) его возвращаемый тип (если есть) должен быть буквальным типом;

(3.2) каждый из его типов параметров должен быть буквальным типом;

(3.3) это не должна быть сопрограмма;

(3.4) если функция является конструктором или деструктором, ее класс не должен иметь никаких виртуальных базовых классов;

(3.5) его функциональное тело не должно заключать

(3.5.1) оператор goto,

(3.5.2) метка идентификатора,

(3.5.3) определение переменной нелитерального типа, либо статической, либо продолжительности хранения потока.

Как видно, определение fне нарушает ни одно из требований в списке. Таким образом, компилятор соответствует требованиям, если он решает не диагностировать это.

Как указано в ответе Ашеплера , constexprтакие функции fне могут быть вызваны в постоянном выражении, но не диагностируются как таковые, считаются плохо сформированными-не-диагностическими.

2 MarshallClow Nov 30 2020 at 22:22

На самом деле вы не «звоните» fво время компиляции.

если ваша основная функция включена: static_assert(f() == 1, "f() returned 1");я подозреваю, что вы получите ошибку «f () не является постоянным выражением».

Вот связанный с этим вопрос

Sneftel Nov 30 2020 at 22:12

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