Различия между decltype (void ()) и decltype (void{})
это продолжение вопроса: Что значит void()
на decltype(void())
значит?.
decltype(void())
компилирует отлично и то, что void()
означает, что в этом случае объясняется в вышеупомянутом вопросе (фактически в ответе).
С другой стороны, я заметил, что decltype(void{})
не компилируется.
в чем разница между ними (в контексте decltype
по крайней мере)?
Почему второе выражение компилировать?
для полноты картины, следует минимальный (не)рабочий пример:
int main() {
// this doesn't compile
//decltype(void{}) *ptr = nullptr;
// this compiles fine
decltype(void()) *ptr = nullptr;
(void)ptr;
}
2 ответов
void()
интерпретируется как Type-id при использовании с sizeof
.void()
интерпретируется как выражение при использовании decltype
.
не думаю void{}
действует в любом контексте. Это не является ни допустимым идентификатором типа, ни допустимым выражением.
(основываясь на обсуждении в комментариях к вопросу)
Примечание: я ссылался на C++17 или близко к ответу. C++14 работает так же, разница в тексте отмечается в конце ответа.
void()
является особым исключением. Увидеть N4618 5.2.3 [expr.тип.conv], выделено мной:
1 A простой-тип-описатель (7.1.7.2) или параметр typename-описатель (14.6) с последующим скобками необязательный выражение-список или braced-init-list (инициализатор) создает значение указанного типа с учетом инициализатор. Если тип является заполнителем для выведенного типа класса, он заменяется возвращаемым типом функции, выбранной разрешением перегрузки для вычета шаблона класса (13.3.1.8) для остальной части этого раздела.
2, Если инициализатор заключен в скобки одиночное выражение, выражение преобразования типа эквивалентно (в определенности и если определено в значении) соответствующему выражению приведения (5.4). если тип (возможно, CV-qualified) void и инициализатор (), выражение является prvalue указанного типа, который не выполняет инициализации. в противном случае выражение является значением prvalue указанного типа, объект результата которого инициализируется напрямую (8.6) с инициализатором. Для выражения вида T () T не должен быть типом массива.
так теперь, 8.6/6 определяет ноль-инициализировать для: N4618 3.9 [basic.типов/9 определяет скаляр: арифметические типы(3.9.1), типы перечислений, типы указателей, указатель на типы членов (3.9.2), std:: nullptr_t, и CV-квалифицированные версии этих типов (3.9.3) в совокупности называются скалярные. N4618 3.9.1 [basic.fundamental] / 8 определяет арифметика типы: интегральные и плавающие типы совместно называются типы арифметических. так помимо инициализация, N4618 3.9.1 [basic.fundamental] / 9 (Болд мой): A тип cv void является неполным типом, что не может быть завершено; такой тип имеет пустой набор ценности. N4618 7.1.7.2 [decl.тип.простой]/5 (Болд мой): если операнд decltype-описатель является prvalue, преобразование временной материализации не применяется (4.4) и объект результата не предоставляется для prvalue. тип prvalue может быть неполным. В C++14, N4296 5.2.3 [expr.тип.conv] по-разному сформулированы. Балочные формы, почти машинально в parenthesised версия: A простой-тип-описатель (7.1.6.2) или параметр typename-описатель (14.6), за которым следует заключенный в скобки выражение-список создает значение указанного типа, заданное в списке выражений. Если список выражений является одним выражением, то выражение преобразования типов эквивалентно (в definedness, и если определено по смыслу) к соответствующему привести выражение (5.4). Если указанный тип является типом класса, то тип класса должен быть полным. Если в списке выражений указано более одного значения, типом должен быть класс с соответствующим объявленным конструктором(8.5, 12.1) и выражением выражение T (), где T - аналогично, a простой-тип-описатель или параметр typename-описатель затем braced-init-list создает временный объект указанного типа direct-list-initialized (8.5.4) с указанным braced-init-list, и его значением является этот временный объект как prvalue. эффект тот же для наших целей,изменить относится к P0135R1 Формулировка для гарантированного копирования elision через упрощенные категории значений, который удалил временное создание объекта из выражений. Вместо этого контекст выражения предоставляет объект результата для инициализации выражением, если контекст нуждается в нем. как отмечалось выше, я чувствую, что исключение в N4618 5.2.3 [expr.тип.conv] должно быть применено к void()
является допустимым только потому, что он явно идентифицирован в [expr.тип.conv] / 2 as нет инициализации. void{}
не соответствует этому исключению, поэтому он пытается быть прямой инициализации это.
void
- это не арифметические типа, так это не скаляр, поэтому он не может быть ноль-инициализировать, поэтому он не может быть значение инициализации, поэтому он не может быть прямой инициализации, поэтому выражение недопустимо.void()
и void{}
будет работать таким же образом, производя prvalue выражение типа void
. Даже если у вас не может быть объект-результат для неполного типа, а void
is всегда неполной:
decltype
специально позволяет неполные типы:
T(x1, x2, ...)
эквивалентно по сути декларации T t(x1, x2, ...)
; для некоторой изобретенной временной переменной t, результатом которой является значение t как prvalue.simple-type-specifier
или typename-specifier
для типа объекта не-массива полного или (возможно CV-квалифицированного) void
type, создает значение prvalue указанного типа, значение которого является значением, полученным путем инициализации значения (8.5) объекта типа T; инициализация не выполняется для случая void (). [Примечание: если T
является неклассовым типом, который имеет квалификацию cv,cv-квалификаторы отбрасываются при определении типа результирующего значения prvalue (пункт 5). -конец Примечание]decltype
(в отличие от sizeof
или typeid
) не предоставляет объект-результат для выражения, поэтому void()
работает, даже если он не может инициализировать результат объект.
void{}
тоже. Это означает, что рекомендации вокруг {}
более сложный. См., например,ES.23: предпочитайте синтаксис {} инициализатора в основных руководящих принципах C++ , которые в настоящее время рекомендуют decltype(void{})
над decltype(void())
. decltype(T{})
скорее всего, будет там, где этот кусает вас...