Ответ 1
Обе реализации прослушиваются, но я склонен думать, что GCC получил правильный ответ здесь.
Снижение захвата i
заставляет Clang отказаться от компиляции кода. Это означает, что у него явно есть ошибка.
Выражение
e
является выражением постоянной константы, если оценка изe
, следуя правилам абстрактной машины, будет оценивать одно из следующих выражений:
- [...]
- в лямбда-выражении, ссылка на [...] переменная с автоматической продолжительностью хранения, определенная вне этого лямбда-выражения, где ссылка будет использовать odr;
- [...]
Поведение Клана шизофренично: если использование i
в теле не является неприемлемым, то его не нужно захватывать, но он отвергает код в OP, если явный захват удален; OTOH, если это одностороннее использование, то приведенное выше unwrap(i)
не является постоянным выражением, поэтому оно должно отклонять инициализацию x
.
Реализация лямбда GCC является ужасно плохим в отношении использования odr. Он постоянно складывается ультра-ранним, в результате чего возникают всевозможные тонкие озорства. С другой стороны, для явных захватов он преобразует все виды использования, независимо от того, действительно ли он использует odr. Агрессивное постоянное складывание означает, что он принимает код OP, если захват i
удален.
Предполагая, что unwrap(i)
использует odr-use i
, то верно, что в [expr.const]/2.12 код OP плохо сформирован.
Использует ли unwrap(i)
odr-use i
? Этот вопрос сводится к тому, чтобы считать, что инициализация копирования объекта параметра unwrap
считается применением преобразования lvalue-to-rval в i
, Я не вижу ничего в стандарте, который явно говорит о том, что здесь применяется преобразование lvalue-rvalue, а вместо этого [dcl.init]/17.6. 2 указывает, что мы вызываем конструктор (в данном случае тривиальный неявно определенный конструктор копирования), передающий i
в качестве аргумента, связанного с его параметром, а привязка ссылок является классическим примером использования odr.
Конечно, применение преобразования l-to-r приведет к инициализации копии объекта integral_constant<int, 42>
из i
, но проблема здесь в том, что ничто в стандарте не говорит обратное - что все копии -инициализации объекта integral_constant<int, 42>
из i
рассчитываются как преобразования l-to-r.