@NirFriedman Нет никаких исключений - просто все, что вы используете в постоянном выражении, должно использоваться в постоянном выражении. Если вы не используете его, это не должно быть.

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

template <class T, class I>
auto foo(const T& t, I i) {
    return std::get<i>(t);
}

int main()
{
    std::cerr << foo(std::make_tuple(3,4), std::integral_constant<std::size_t, 0>{});
    return 0;
}

Живой пример:http://coliru.stacked-crooked.com/a/fc9cc6b954912bc5.

Кажется, работает с gcc и clang. Дело в том, что покаintegral_constant имеетconstexpr преобразование в сохраненное целое число,constexpr функции-члены неявно принимают сам объект в качестве аргумента, и поэтому такая функция не может быть использована вconstexpr контекст, если объект, который мы вызываем саму функцию-член, не может рассматриваться какconstexpr.

Вот,i аргумент переданfoo, и поэтомуi безусловно, не может рассматриваться какconstexpr, Тем не менее, это так. Еще более простой пример:

template <class I>
void foo(I i) {
    constexpr std::size_t j = i;
}

Это тоже компилируется, покаstd::integral_constant<std::size_t, 0>{} передаетсяfoo.

Я чувствую, что упускаю что-то очевидное вconstexpr правила. Есть ли исключение для типов без состояний или что-то еще? (или, может быть, ошибка компилятора в двух основных компиляторах? Этот код работает на clang 5 и gcc 7.2).

Изменить: ответ был опубликован, но я не думаю, что это достаточно. В частности, учитывая последнее определениеfoo, Почему:

foo(std::integral_constant<std::size_t, 0>{});

Компилировать, но не:

foo(0);

И 0 иstd::integral_constant<std::size_t, 0>{} являются постоянными выражениями.

Редактировать 2: Кажется, это сводится к тому, что вызовconstexpr Функция-член даже для объекта, который не является константным выражением, сама может рассматриваться как константное выражение, если толькоthis не используется. Это воспринимается как очевидное. Я не считаю это очевидным:

constexpr int foo(int x, int y) { return x; }

constexpr void bar(int y) { constexpr auto x = foo(0, y); }

Это не компилируется, потому чтоy как перешли вfoo не является постоянным выражением. Это не используется, не имеет значения. Таким образом, полный ответ должен показать какой-то язык из стандарта, чтобы оправдать тот факт, чтоconstexpr Функция-член может использоваться как константное выражение, даже для объекта с неконстантным выражением, еслиthis не используется.

 Justin20 сент. 2017 г., 20:04
Это интересно;это может быть использовано для реализацииoperator[] заstd::tupleи объединение его с неким целочисленным константным литералом сделало бы его еще более понятным (tuple[0_c]).
 Justin19 сент. 2017 г., 00:28
Работает и на Visual C ++:godbolt.org/g/Cknxco
 Nir Friedman19 сент. 2017 г., 00:30
@Justin Хороший улов, хотя, учитывая, что это icc, держу пари, это еще одна не связанная ошибка :-).
 Justin19 сент. 2017 г., 00:29
Кажется, не работает на компиляторе Intel:godbolt.org/g/wLc5dw

Ответы на вопрос(2)

Причина этого работает:

template <class T, class I>
auto foo(const T& t, I i) {
    return std::get<i>(t);
}

потому что ни одна из причин, по которой он потерпит неудачу, не применяется. когдаi этоstd::integral_constant<size_t, S>, i может использоваться как преобразованное константное выражение типаsize_t потому что это выражение проходит черезconstexpr operator size_t(), который просто возвращает параметр шаблона (который являетсяprvalue) в качестве значения. Это совершенно правильное константное выражение. Обратите внимание, чтоthis на него не ссылаются - просто потому, что это функция-член, сама по себе не нарушает ограничение константного выражения.

В принципе, в этом нет ничего «временного»i Вот.

С другой стороны, еслиi былиint (в порядкеfoo(0)), то звонюstd::get<i> будет включать преобразование lvalue в rvalue дляi, но этот случай не встретитсялюбой из критериев посколькуi не имеет предшествующей инициализации с помощью константного выражения, это не строковый литерал, он не был определен сconstexprи он не начал свою жизнь в этом выражении.

 Barry19 сент. 2017 г., 01:13
@NirFriedman Нет никаких исключений - просто все, что вы используете в постоянном выражении, должно использоваться в постоянном выражении. Если вы не используете его, это не должно быть.
 Ben Voigt19 сент. 2017 г., 00:59
Правильно, важно то, что значение извлекается из параметра шаблона, а не аргумента функции.
 Barry19 сент. 2017 г., 00:59
@cpplearner Да, всегда, забудь об этом. Ура, исправлено
 Nir Friedman19 сент. 2017 г., 01:09
Я отредактировал свой вопрос; как я прокомментировал ниже, тот факт, чтоthis не используется, не имеет значения при любой наивной интерпретации того, что будут делать функции-члены. Как правило,constexpr функция требуетвсе из его аргументов, чтобы быть константными выражениями, которые будут использоваться в качестве константного выражения. Ситуация сthis в основном исключение, и в стандарте должен быть какой-то язык, утверждающий, что это нормально.
Решение Вопроса

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

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

Условное выражение e является основным константным выражением, если при вычислении e, следуя правилам абстрактной машины, не будет выполнено одно из следующих выражений:

...

преобразование lvalue в rvalue, если оно не применяется кэнергонезависимое glvalue целочисленного типа или типа перечисления, которое относится к полному энергонезависимому объекту const с предшествующей инициализацией, инициализированной с помощью константного выражения, илиэнергонезависимое glvalue, которое ссылается на подобъект строкового литерала, илиэнергонезависимое glvalue, которое относится к энергонезависимому объекту, определенному сconstexprили это относится к неизменяемому подобъекту такого объекта, илиэнергонезависимое glvalue литерального типа, которое относится к энергонезависимому объекту, время жизни которого началось в пределах оценки e;

Вы правы, что третья подпулета не применяется. Но первый делает.

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

Вот быстрый пример, почему это не имеет значения,this является неявным аргументом. Быть аргументом не означает, что объект оценивается:

constexpr int blorg(bool const flag, int const& input)
{
    return flag? 42: input;
}

int i = 5; // 5 is an integral constant expression, but `i` is not
constexpr int x = blorg(true, i); // ok, `i` was an argument but never evaluated
constexpr int y = blorg(false, i); // no way

Заstd::integral_constant функции-члены, вы можете рассмотреть*this любитьi вblorg function - если выполнение не разыменовывает его, можно передавать его, не будучи константой времени компиляции.

 Nir Friedman19 сент. 2017 г., 00:44
Я немного подумал об этом ответе, мне кажется, что если этот ответ правильный, то мои рассуждения будут такими:foo(0) в моем обновленном вопросе тоже надо скомпилировать, но это не так. Я думаю, что точное понимание различий между двумя вызовами в моей редакции позволит четко ответить на этот вопрос.
 cpplearner19 сент. 2017 г., 00:45
Я думаю, что ничего из этого не применимо, потому что нет преобразования lvalue в rvalue.
 Ben Voigt19 сент. 2017 г., 00:53
@cpplearner: в неудачном преобразовании есть преобразование lvalue в rvaluefoo(0) вызов. Но в другом коде, основанном на шаблонах, есть только параметры шаблонов именования, которые являются правильными и не являются lvalues ​​и поэтому не нуждаются в преобразовании.
 Nir Friedman19 сент. 2017 г., 00:54
@BenVoigt Хорошо, хорошо. Тогда ключевым моментом (с моей точки зрения) является то, что функция-член constexpr не имеет доступаthis может использоваться в постоянных контекстах, даже если вызываемый объект не является константным выражением. Это противоречит наивной интерпретацииthis считается неявным аргументом для всех функций-членов. Можете ли вы найти формулировку в стандарте, который поддерживает это? Если ты это сделаешь, я приму.
 Ben Voigt19 сент. 2017 г., 00:52
@NirFriedman: В тех случаях, когда это работает, единственное, что оценивается, это встроенный параметр шаблона, который попадает в подпункт 1. В вашем новом коде прерывания оценивается параметр переменной времени выполнения. Вызов не виртуальной функции-члена не обязательно оценивает выражение, для которого она вызывается ...std::integral_constant операторы преобразования не имеют доступа к элементам нестатических данныхthisи, следовательно, не становятся незаконными в постоянном контексте.

Ваш ответ на вопрос