Выражения без побочных эффектов в C++

Видите ли, чего я не понимаю, почему такие программы, как следующие, должны быть законными?

int main()
{ 
    static const int i = 0;
    i < i > i;
}

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

Другой пример такой:

int main() {
    static const int i = 0;
    int x = (i);
}

Какова реальная польза от таких заявлений?

И вещи как самый неприятный разбор. Кто-нибудь когда-нибудь объявлял функции посреди других функций? Я имею в виду, что мы избавились от таких вещей, как неявное объявление функции и тому подобное. Почему бы просто не избавиться от них для C++0x?


person Puppy    schedule 10.11.2010    source источник


Ответы (7)


это значительно упростило бы синтаксический анализ и компиляцию языка.

Я не понимаю, как. Почему проще разобрать и скомпилировать i < i > i, если вам требуется выдать диагностику, чем разобрать ее, если вам разрешено делать все, что вы чертовски хорошо, пожалуйста, при условии, что сгенерированный код никаких побочных эффектов?

Компилятор Java запрещает недостижимый код (в отличие от кода без эффекта), что является смешанным благом для программиста и требует от компилятора немного дополнительной работы по сравнению с тем, что на самом деле требуется компилятору C++ (базовая зависимость блоков анализ). Должен ли C++ запрещать недостижимый код? Возможно нет. Несмотря на то, что компиляторы C++, безусловно, выполняют достаточную оптимизацию для выявления недостижимых базовых блоков, в некоторых случаях они могут делать слишком много. Должен ли if (foo) { ...} быть недопустимым недостижимым блоком, если foo является ложной константой времени компиляции? Что, если это не константа времени компиляции, но оптимизатор понял, как вычислить значение, должно ли оно быть допустимым, и компилятор должен понимать, что причина, по которой он удаляет ее, зависит от реализации, чтобы не выдать ошибку ? Еще частные случаи.

на самом деле ни у кого нет текущих программ, в которых есть выражения без побочных эффектов.

Нагрузки. Например, если NDEBUG истинно, то assert преобразуется в пустое выражение без какого-либо эффекта. Таким образом, в компиляторе требуется еще больше особых случаев, чтобы разрешить некоторые бесполезные выражения, но не другие.

Обоснование, я считаю, заключается в том, что если бы он расширялся до нуля, то (а) компиляторы в конечном итоге выдавали бы предупреждения для таких вещей, как if (foo) assert(bar);, и (б) подобный код был бы легальным в выпуске, но не в отладке, что просто сбивает с толку:

assert(foo) // oops, forgot the semi-colon
foo.bar();

такие вещи, как самый неприятный разбор

Вот почему это называется «раздражать». На самом деле это проблема обратной совместимости. Если бы C++ теперь изменил значение этих надоедливых синтаксических анализов, значение существующего кода изменилось бы. Как вы указываете, существующего кода не так много, но комитет C++ занимает довольно сильную позицию в отношении обратной совместимости. Если вам нужен язык, который меняется каждые пять минут, используйте Perl ;-)

В любом случае, теперь уже слишком поздно. Даже если бы у нас было какое-то прекрасное понимание, которое пропустил комитет C++0x, почему некоторые функции должны быть удалены или изменены несовместимо, они не собираются ничего ломать в FCD, если только FCD не является окончательно ошибочным.

Обратите внимание, что для всех ваших предложений любой компилятор может выдать предупреждение для них (на самом деле, я не понимаю, в чем ваша проблема со вторым примером, но, конечно, для бесполезных выражений и для раздражающих синтаксических анализов в телах функций). Если вы правы в том, что никто не делает этого преднамеренно, предупреждения не причинят вреда. Если вы ошибаетесь в том, что никто не делает этого преднамеренно, указанный вами случай их удаления неверен. Предупреждения в популярных компиляторах могут проложить путь к удалению функции, тем более что авторами стандарта в основном являются разработчики компиляторов. Тот факт, что мы не всегда получаем предупреждения об этих вещах, наводит меня на мысль, что это нечто большее, чем вы думаете.

person Steve Jessop    schedule 10.11.2010

Вероятно, потому что запрет сделал бы спецификацию более сложной, что сделало бы компиляторы более сложными.

person Douglas Leeder    schedule 10.11.2010
comment
За исключением того, что если у компилятора есть два способа разобрать оператор, но один из них недопустим, то он знает, что это должен быть другой, который прост, тогда как если стандарт допускает оба, то компилятор должен решить, какой из них что гораздо менее просто. Чем менее жизнеспособны программы, тем меньше логики вам нужно, чтобы решить, какая у вас есть. - person Puppy; 10.11.2010
comment
Однако это не всегда так, поскольку компиляторы должны обнаруживать определенные недопустимые выражения и сообщать о соответствующих сообщениях об ошибках. В любых случаях, как вы предлагаете, он, безусловно, не может предполагать, что код действителен. - person Douglas Leeder; 10.11.2010

  • Иногда удобно поместить бесполезные операторы в программу и скомпилировать ее только для того, чтобы убедиться, что они законны - например. что используемые типы могут быть разрешены/сопоставлены и т. д.

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

  • Может быть какой-то временно закомментированный код, который удаляет осмысленное использование переменной, но это может быть проблемой, чтобы аналогичным образом идентифицировать и комментировать все переменные, которые не используются в другом месте.

  • Хотя в ваших примерах вы показываете переменные как «int» непосредственно над бессмысленным использованием, на практике типы могут быть намного сложнее (например, оператор‹()), и компилятор может даже знать, имеют ли операции побочные эффекты (например, нестандартные функции), поэтому любые преимущества ограничены более простыми случаями.

  • C++ нужна веская причина, чтобы нарушить обратную (и сохранить C) совместимость.

person Tony Delroy    schedule 10.11.2010

Почему ничегонеделание должно рассматриваться как частный случай? Кроме того, в то время как описанные выше случаи легко заметить, можно представить гораздо более сложные программы, в которых не так просто определить, что нет побочных эффектов.

person Oliver Charlesworth    schedule 10.11.2010

Как итерация стандарта C++, C++0x должен быть обратно совместим. Никто не может утверждать, что заявлений, которые вы написали, не существует в каком-то важном программном обеспечении, написанном или принадлежащем, скажем, НАСА или Министерству обороны.

В любом случае, что касается вашего самого первого примера, анализатор не может утверждать, что i является статическим константным выражением, а i < i > i является бесполезным выражением, например. если i является шаблонным типом, i < i > i является "недопустимым объявлением переменной", а не "бесполезным вычислением" и все же не ошибкой синтаксического анализа.

person Noe    schedule 10.11.2010
comment
Никто не говорит, что НАСА или Министерство обороны должны перейти на C++0x. Если им это нравится, они могут оставить C++03. Старые версии компилятора не исчезнут волшебным образом. - person Puppy; 10.11.2010

Возможно, оператор был перегружен, чтобы иметь побочные эффекты, такие как cout<<i;. Поэтому их нельзя удалить сейчас. С другой стороны, C# запрещает использование выражений без присваивания или вызовов методов в качестве операторов, и я считаю, что это хорошо, поскольку делает код более ясным и семантически правильным. Однако у C# была возможность запретить это с самого начала, чего нет у C++.

person Stilgar    schedule 10.11.2010

Выражения без побочных эффектов могут появляться чаще, чем вы думаете, в коде шаблонов и макросов. Если вы когда-либо объявляли std::vector<int>, вы создали экземпляр кода шаблона без побочных эффектов. std::vector должен уничтожить все свои элементы при освобождении, если вы сохранили класс для типа T. Это требует в какой-то момент оператора, подобного ptr->~T();, для вызова деструктора. Однако int не имеет деструктора, поэтому вызов не имеет побочных эффектов и будет полностью удален оптимизатором. Также вероятно, что это будет внутри цикла, тогда весь цикл не имеет побочных эффектов, поэтому весь цикл удаляется оптимизатором.

Поэтому, если вы запретите выражения без побочных эффектов, std::vector<int> не будет работать, например.

Другим распространенным случаем является assert(a == b). В релизных сборках вы хотите, чтобы эти утверждения исчезли, но вы не можете переопределить их как пустой макрос, иначе такие операторы, как if (x) assert(a == b);, внезапно помещают следующий оператор в оператор if - катастрофа! В этом случае assert(x) можно переопределить как ((void)0), что является оператором, не имеющим побочных эффектов. Теперь оператор if корректно работает и в релизных сборках — он просто ничего не делает.

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

person AshleysBrain    schedule 10.11.2010