Arhn - архитектура программирования

Почему это выражение C++ не генерирует даже предупреждения?

Я знаю, что вы можете подавить предупреждение «выражение без эффекта», приведя выражение к void:

int main()
{
    void(2+3);
}

Если я не ошибаюсь, смысл приведения чего-то к void заключается не в приведении выражения к объекту типа void (void — это тип без объектов или, лучше сказать, пустой набор), а в сообщении компилятору, что мы хотим игнорировать значение выражения. Другой связанный образец:

int main()
{
    (void)(2+3);
}

есть ли семантическая разница между этим и другим образцом?

И наконец:

int main()
{
    void();
}

который не возвращает предупреждений (здесь Coliru test, полный педантичных вариантов); однако этот другой генерирует ошибку:

int main()
{
    (void)();
}

// Error:
//    main.cpp:6:9: error: expected primary-expression before 'void'
//       (void)();
//        ^
//    main.cpp:6:9: error: expected ')' before 'void'

Что означает выражение void()? Вы создаете временный объект типа void, который не имеет смысла?, или вы приводите пустое выражение к void?

И вообще, я хотел бы понять полную картину об использовании void и о том, какое поведение/семантика соответствует стандарту.

12.10.2014

  • Обратите внимание, что стандарт (по крайней мере, не в C, не могу быть уверенным в C++) не требует этой диагностики; просто авторы компиляторов помогают. Таким образом, вы не найдете ничего в стандарте, который касается этого. 12.10.2014
  • @OliverCharlesworth Спасибо. Я обновлю свой вопрос с вашим комментарием. 12.10.2014
  • однако этот другой выдает ошибку - в вопросе нет ошибки. Если вы упомянули ошибку, пожалуйста, опубликуйте ее! 12.10.2014
  • Фактически, (void)unused_arg — это распространенный способ отключить предупреждение о неиспользуемом аргументе в конкретной функции, если вы все же хотите, чтобы такие предупреждения появлялись в других местах. 12.10.2014
  • Например, gcc говорит для -Wunused-value: To suppress this warning cast the unused expression to ‘void’.. 12.10.2014

Ответы:


1

Этот void(2+3) не является приведением, это псевдо конструктор. Да, между void(2+3) и (void)(2+3) есть семантическая разница. void(2+3) — это конструкция с неявным приведением типа: void((void)(2+3)). void() также является допустимой псевдоконструкцией, хотя и с псевдоконструктором по умолчанию, а не с псевдоконструктором копирования.

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

Приведения работают с выражениями: (void) (2+3) работает с выражением (2+3). Но () недопустимое выражение, поэтому (void)() недопустимо. Точно так же вы не можете сделать static_cast<void>() или static_cast<void>(()).

12.10.2014
  • Псевдоконструкторы @Peregring-lk поддерживают синтаксис построения копирования. Например. int(1). Как и в случае с обычными конструкторами копирования, компилятор неявно выполняет преобразование из того, что вы передаете, в тип параметра конструктора копирования. Например. int(10.0) неявно преобразует 10.0 из double в int. То же самое и с псевдоконструктором void(2+3): компилятор вставляет неявное приведение к типу void: void((void)(2+3)). Когда вы используете синтаксис void(), он использует не конструктор псевдокопий, а конструктор псевдо по умолчанию, поэтому неявное приведение отсутствует. 12.10.2014
  • @bames53 К черту пустоты. Но да, у меня есть идея. Большое спасибо. Тем не менее, я должен внимательно прочитать семантику приведения и C-подобного приведения, и, прежде всего, о временных объектах, потому что концепция неприсваиваемых временных объектов немного сумасшедшая. Например, использование void() в некоторых выражениях, таких как T tmp((<some-side-effect>, void(), <tmp-value>)), чтобы избежать перегрузок оператора запятой; Я понимаю вопрос, но этот несуществующий объект в эфире кажется мне чем-то мистическим. 12.10.2014
  • @AndreyT Правда, для этого есть правило, § [stmt.return] 6.6.3/3, но на самом деле ничего не делать с временным void не-объектом. 12.10.2014
  • Я никогда раньше не слышал термин псевдоконструктор. Стандарт C++ называет это выражение явным преобразованием типа (функциональная нотация) и говорит в [expr.type.conv]/1 спецификатор простого типа или спецификатор имени типа, за которым следует заключенный в скобки список-выражений, создает значение указанного типа с учетом списка выражений. Если список выражений является одним выражением, выражение преобразования типа эквивалентно (по определению и, если оно определено по смыслу) соответствующему выражению приведения. Итак, это это приведение. 12.10.2014
  • @dyp На самом деле, согласно справочнику по C++ (я только что читал там), синтаксис T(expr) эквивалентен приведению C-стиля (T)(expr), что эквивалентно попытке const_cast, static_cast и reinterpreter_cast в этот порядок (если выбран static_cast, то между другими возможностями может быть вызван конструктор копирования). Я предполагаю, что только если внутри нет выражения, выполняется прямой вызов конструктора по умолчанию. 13.10.2014
  • @Peregring-lk Это звучит правильно, но последняя часть о конструкторе по умолчанию — это еще не вся история: в форме T() создается prvalue (временный, но не обязательно объект), который инициализируется значением . Инициализация значения может вызвать конструктор по умолчанию, но только в некоторых случаях. Даже для типов классов, если конструктор по умолчанию тривиален, он не вызывается для инициализации значений. 13.10.2014
  • @dyp Инициализировано значением или инициализировано нулем? Для инициализации значения вам нужно значение, не так ли? И второе: какие временные объекты не являются объектами? 13.10.2014
  • @Peregring-lk Значение инициализировано. Нет, вам не нужно передавать значение для инициализации значения. Как говорит dyp, T() создает значение prvalue даже для таких вещей, как void(), так что это пример необъектного «временного». 13.10.2014
  • @Peregring-lk Инициализация значения - это просто имя;) Нулевая инициализация не вызывает конструкторы, поэтому T() не будет правильно создавать сложные типы классов. Однако инициализация значения может выполнять инициализацию нулями (например, для фундаментальных типов и типов классов с тривиальными конструкторами по умолчанию). 13.10.2014
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..