Критерии для использования переменной в константном выражении

Этот код прекрасно компилируется на всех больших 4-х компиляторах, даже на -pedantic

struct S
{
  constexpr S(int a) {}
};

constexpr int f(S a)
{
  return 1;
}

int main()
{
  int a = 0;
  S s(a);
  constexpr int b = f(s);
}

Тем не менее, это не должно быть так в соответствии со стандартом... верно? Во-первых, s нельзя будет использовать в константных выражениях [expr.const]/3, потому что он не соответствует критериям либо constexpr, либо const и перечисления или интегральный тип.

Во-вторых, он не инициализирован константой [expr.const]/2, поскольку полное выражение инициализации не будет константным выражением [expr.const]/10 из-за Преобразование lvalue в rvalue выполняется для переменной (a), которую нельзя использовать в константных выражениях при инициализации параметра конструктора.

Все ли эти компиляторы просто исключают инициализацию параметра конструктора, потому что он не имеет побочных эффектов и соответствует ли он стандарту (я уверен, что это не так на 99%, так как единственный способ это сделать - это сделать s constexpr и передать ему const int или int, который объявлен constexpr)?

Ответ 1

Я полагаю, что фокус фокусника здесь является копией S. Вы пропустили его, поэтому для вас генерируется дефолтный. Теперь это тоже функция constexpr.

[class.copy.ctor] (emphasis mine)

12 Конструктор копирования/перемещения, который по умолчанию не определен как удаленный неявно определяется, когда он используется odr ([basic.def.odr]), когда это необходимо для постоянной оценки ([expr.const]) или когда явно по умолчанию после его первого объявления. [ Обратите внимание Конструктор копирования/перемещения определяется неявно, даже если реализация исключил его использование odr ([basic.def.odr], [class.teven]). - конец примечания] Если неявно определенный конструктор будет удовлетворять требованиям конструктора constexpr ([dcl.constexpr]), неявно определенный конструктор - constexpr.

Не попала ли оценка копии какой-либо из точек в [expr.const]/4? Это не. Он не выполняет преобразование lvalue в rvalue ни на одном из членов аргумента (нет ни одного, с кем можно выполнить преобразование). Он не использует свой параметр ссылки никоим образом, который потребует использования указанной ссылки в константном выражении. Таким образом, мы действительно получаем правильное константное выражение, хотя и неинтуитивное.

Мы можем проверить вышеизложенное, просто добавив участника в S.

struct S
{
  int a = 1;  
  constexpr S(int a) {}
};  

Теперь копия c'tor пытается получить доступ к объекту, который нельзя использовать в константном выражении, как часть его оценки (по указанной ссылке). Таким образом, компиляторы будут жаловаться.