clang 5: std :: необязательные винты реализации std :: is_constructible черта типа параметра

Действительно странное и неожиданное поведение clang 5 было обнаружено при переходе на c ++ 17 и замене пользовательских std::optional решение со стандартным. По какой-то причине emplace() был отключен из-за неправильной оценки std::is_constructible черта класса параметров.

Некоторые конкретные предварительные условия должны быть выполнены, прежде чем он воспроизводит:

#include <optional>

/// Precondition #1: T must be a nested struct
struct Foo
{
struct Victim
{
/// Precondition #2: T must have an aggregate-initializer
/// for one of its members
std::size_t value{0};
};

/// Precondition #3: std::optional<T> must be instantiated in this scope
std::optional<Victim> victim;

bool foo()
{
std::optional<Victim> foo;

// An error
foo.emplace();
/// Assertion is failed
static_assert(std::is_constructible<Victim>::value);
}
};

Живой пример на godbolt.org


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

Как примечание стороны: GCC 7.1 а также GCC 7.2 нет проблем с приведенным выше кодом.


Сообщение об ошибке по адресу: bugs.llvm.org

16

Решение

Это похоже на ошибку компилятора. От [учебный класс]

Класс считается полностью определенным типом объекта (или завершенным типом) при закрытии } спецификатора класса.

Что значит Victim завершено в std::optional<Victim>, делая его ничем не отличающимся от любого другого типа в этом контексте.

От [мета]

Условие предиката для специализации шаблона is_­constructible<T, Args...> должны быть выполнены тогда и только тогда, когда следующее определение переменной будет правильно сформировано для некоторой изобретенной переменной t:
T t(declval<Args>()...);

Который является прямой инициализацией t с аргументами типа Args..., или если sizeof...(Args) == 0это инициализация значения t,

В этом случае, Значение инициализации t по умолчанию инициализировать t, что справедливо отсюда std::is_constructible_v<Victim> должно быть правдой.

С учетом всего сказанного, компиляторы кажутся борющийся много составление этого.

7

Другие решения

Хорошо, выкопал соответствующие цитаты. Суть вопроса в том, как std::is_constructible должен справиться Victim, Наиболее убедительным авторитетом является C ++ 17 (n4659). Первый [Meta.unary.prop / 8]:

Условие предиката для специализации шаблона
is_­constructible<T, Args...> должны быть выполнены, если и только если
Следующее определение переменной будет правильно сформировано для некоторых изобретенных
переменная т:

T t(declval<Args>()...);
[ Заметка: Эти токены никогда не интерпретируются как объявление функции.
 - конец примечания] Проверка доступа выполняется как в контексте
не связанный с T и любым из Args. Только действительность немедленного
контекст инициализации переменной рассматривается.

Заметка, которую я выделил, не является нормативной (из-за того, что она является заметкой), но совпадает с [Temp.variadic] / 7:

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

Так что для целей is_­constructible, этот T t(); действительно делает t объявление переменной. Эта инициализация является инициализацией значения, потому что [Dcl.init / 11] говорит так же:

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

Это означает, что черта в конечном итоге проверяет, Victim может быть инициализирован значением. Что это может. Это агрегат, но неявно заданный по умолчанию c’or по умолчанию все еще определяется компилятором (очевидно, для поддержки инициализации значения).

Короче. У Clang есть ошибка, вы должны сообщить об этом.

3

По вопросам рекламы [email protected]